diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8275ac8b0740..7c75f0eef805 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -33,7 +33,7 @@ jobs:
name: Upload development build
with:
name: "Development Build"
- path: versions/1.8.9/build/libs/*.jar
+ path: build/libs/*.jar
- name: Test with Gradle
run: ./gradlew test
- uses: actions/upload-artifact@v3
diff --git a/.github/workflows/check_dependencies.yml b/.github/workflows/check_dependencies.yml
index 50f3641ae03c..0d79d26abfd2 100644
--- a/.github/workflows/check_dependencies.yml
+++ b/.github/workflows/check_dependencies.yml
@@ -1,11 +1,9 @@
name: Check PR Dependencies
+# TODO: Run when other pr gets merged
on:
pull_request_target:
types: [ opened, edited ]
- push:
- branches:
- - beta
jobs:
check-dependencies:
diff --git a/.idea/dictionaries/default_user.xml b/.idea/dictionaries/default_user.xml
index b166e60d38fd..cb91301718f0 100644
--- a/.idea/dictionaries/default_user.xml
+++ b/.idea/dictionaries/default_user.xml
@@ -81,6 +81,7 @@
explosivity
ezpz
fairylosopher
+ fels
fermento
firedust
firesale
@@ -100,6 +101,7 @@
hideparticles
hoppity
hoppity's
+ hoppitys
horsezooka
hotbar
hotm
@@ -145,6 +147,7 @@
millenia
minecart
mineman
+ mineshafts
miniboss
mirrorverse
misclick
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 57f22f2e2583..fc0f2d5c4f90 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,8 +30,54 @@ We use [IntelliJ](https://www.jetbrains.com/idea/) as an example.
### Setting up IntelliJ
-SkyHanni's Gradle configuration is very similar to the one used in **NotEnoughUpdates**, just
-follow [their guide](https://github.com/NotEnoughUpdates/NotEnoughUpdates/blob/master/CONTRIBUTING.md).
+Once your project is imported into IntelliJ from the previous step, all dependencies like Minecraft, NEU, and so on should be automatically
+downloaded. If not, you might need to link the Gradle project in the Gradle tab (little elephant) on the right.
+
+
+🖼️Show Gradle tab image
+
+![Gradle tab with Link Project and Gradle Settings highlighted](docs/gradle-tab.jpg)
+
+
+
+If importing fails, make sure the Gradle JVM (found in the settings wheel in the Gradle tab, or by searching Ctrl + Shift + A
+for "Gradle JVM") is set to a Java 21 JDK. While this is not the version of Java Minecraft 1.8.9 uses, we need this version for some of our
+build tools.
+
+
+🖼️Show Gradle JVM image
+
+![Gradle settings showing Java 21 being selected as JVM](docs/gradle-settings.png)
+
+
+
+After all importing is done (which might take a few minutes the first time you download the project), you should find a new IntelliJ run
+configuration.
+
+
+🖼️Show run configuration selection image
+
+![Where to select the run configuration](docs/minecraft-client.webp)
+
+
+
+That task might work out of the box, but very likely it will not. We will need to adjust the used Java version. Since Minecraft 1.8.9 uses
+Java 1.8, we will need to adjust the used JDK for running our Mod, as well as potentially changing the argument passing style.
+
+So select an appropriate Java 1.8 JDK (preferably [DCEVM](#hot-swap), but any Java 1.8 JDK or even JRE will do) and select None as the
+argument passing style.
+
+
+🖼️Show run configuration image
+
+![Run configuration settings](docs/run-configuration-settings.avif)
+
+
+
+Now that we are done with that, you should be able to launch your game from your IDE with that run configuration.
+
+SkyHanni's Gradle configuration is very similar to the one used in **NotEnoughUpdates**, so if you want to look at another guide, check
+out [their guide](https://github.com/NotEnoughUpdates/NotEnoughUpdates/blob/master/CONTRIBUTING.md).
## Creating a Pull Request
diff --git a/README.md b/README.md
index a7c00b495c57..07bf336a172c 100644
--- a/README.md
+++ b/README.md
@@ -14,19 +14,19 @@
## What it does
-SkyHanni is a Forge mod for Minecraft 1.8.9 that adds many useful features to Hypixel SkyBlock. With SkyHanni, you'll get:
+SkyHanni is a Forge mod for Minecraft 1.8.9 that adds many useful features to [Hypixel SkyBlock](https://wiki.hypixel.net/Main_Page). With SkyHanni you have access to:
-* **Helpful GUIs:** Access important information at a glance.
-* **Extra Chat Messages:** Receive reminders and helpful tips.
-* **Message Hiders:** Control which messages you see in chat.
-* **Entity/Item Highlighters:** Focus on important mobs or items in the world/your inventory.
-* **[And much more!](docs/FEATURES.md)**
+* **Helpful GUIs:** View important information at a glance.
+* **Extra Chat Messages:** Receive reminders and tips at the right moment.
+* **Object Highlighters:** Focus on important items in inventories or highlight mobs in the world.
+* **Highly Customizeable Displays:** Personalise your Scoreboard, Tab List or chat format.
+* [And **much** more!](docs/FEATURES.md)
-SkyHanni is especially helpful when doing activities like farming, slayers, Bingo, Diana, fishing, or Rift.
+SkyHanni is especially useful when doing farming, slayers, Bingo, Diana, fishing, Rift or mining.
## Getting Started
-1. **Install:** Check out the [installation guide](docs/INSTALLING.md).
+1. **Install:** Follow the [installation guide](docs/INSTALLING.md).
2. **Set Up:** Type `/sh` or `/skyhanni` in-game to configure your settings.
3. **Explore:** See all the features [here](docs/FEATURES.md).
@@ -34,17 +34,17 @@ SkyHanni is especially helpful when doing activities like farming, slayers, Bing
Give feedback or just chat with others on our community Discord!
-* **Bug Reports:** Use the `#bug-reports` channel.
-* **Feature Suggestions:** Use the `#suggestions` channel.
-* **General Chat:** Chat with other SkyHanni users in `#skyblock-general` channel.
+* **Bug Reports:** Use the `#bug-reports` channel when you find broken features (please check out `#faq` and `#known-bugs`).
+* **Quick Help** Ask in `#support` for questions and problems with the the mod or Minecraft in general.
+* **Feature Suggestions:** Feel fre to tell your ideas in `#suggestions` channel for new features and improvements to the mod. (Don't copy from existing mods or break Hypixel rules).
+* **General Chat:** Chat with other SkyHanni users in `#skyblock-general` channel about the game.
-[Join the Discord](https://discord.gg/skyhanni-997079228510117908)
+**[Join the Discord!](https://discord.gg/skyhanni-997079228510117908)**
## Contributing
-Interested in writing your own SkyHanni feature or fixing that one annoying bug yourself? Check out our [contributing guide](CONTRIBUTING.md) for more information.
+Are you interested in writing your own SkyHanni feature? Do you want to fix that one annoying bug yourself? Check out our [contributing guide](CONTRIBUTING.md) for more information!
---
-**SkyHanni is part of an active modding community. Explore other useful mods [here](https://sbmw.ca/mod-lists/skyblock-mod-list/) to
-complete your SkyBlock setup!**
+**SkyHanni is part of an active modding community. Explore other useful mods [here](https://sbmw.ca/mod-lists/skyblock-mod-list/) for even more SkyBlock features!**
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index b287d2b74708..0d26050da8b3 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,6 +1,300 @@
# SkyHanni - Change Log
-## Version 0.26 (In Beta)
+## Version 0.27 (in Beta)
+
+### New Features
+
+#### Garden Features
+
++ Added No Pests Chat Filter. - saga (https://github.com/hannibal002/SkyHanni/pull/1957)
+ + Removed the chat message "There are no Pests on your Garden!".
++ Added No Pests Title. - saga (https://github.com/hannibal002/SkyHanni/pull/1957)
+ + Shows a title when you use the Pest Tracker without any pests to clear.
+
+#### Mining Features
+
++ Added a "Get from Sack" button in the forge recipe menu to retrieve ingredients. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2106)
+
+#### Rift Features
+
++ Added Motes per Session. - Empa (https://github.com/hannibal002/SkyHanni/pull/2323)
++ Added Crafting Room Helper. - HiZe (https://github.com/hannibal002/SkyHanni/pull/2178)
+ + Shows a holographic mob at the location where the mob is present in the real room inside the Mirrorverse in Rift.
++ Added Rift Time Real-Time Nametag Format. - Empa (https://github.com/hannibal002/SkyHanni/pull/2015)
+
+#### Dungeon Features
+
++ Added highlight for starred dungeon mobs. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1558)
++ Added highlight for Fel skulls. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1558)
+ + Optionally draws a line to them as well.
++ Added a Secret Chime for Dungeons with adjustable pitch and sound. - Ovi_1 (https://github.com/hannibal002/SkyHanni/pull/2478)
+ + The sound and pitch of chimes in dungeons are customizable.
+
+#### Scoreboard Features
+
++ Added Soulflow to the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
+ + Requires Soulflow to be enabled in Hypixel settings: /tab -> Profile Widget -> Show Soulflow.
+
+#### Hoppity Features
+
++ Added Hoppity Hunt event summary. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
+ + Use /shhoppitystats for live stats.
++ Added optional warning when Hoppity calls you with a rabbit to sell. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
++ Added hotkey for picking up Abiphone calls from Hoppity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
+
+#### GUI Features
+
++ Added Editable XP Bar. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1944)
+ + Enabled moving and scaling of the XP bar in the SkyHanni GUI Editor.
++ Added Display Tab Widgets. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1276)
+ + Allows the display of information from the Tab (e.g., Bestiary info).
+
+#### Inventory Features
+
++ Added a warning when opening the Experimentation Table without a Guardian pet. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2127)
++ Added Enchanting Experience as Stack Size in Experimentation Table. - saga (https://github.com/hannibal002/SkyHanni/pull/1988)
+ + Added to the Item Number list.
++ Show dye hex code as the actual color in the item lore. - nopo (https://github.com/hannibal002/SkyHanni/pull/2321)
++ Added Display for Bits on Cookie buy. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2265)
+ + Shows the Bits you would gain.
+ + Can show the change for the available Bits.
+ + Also shows the time more clearly (or adds it if not present).
++ Added Compact Experimentation Table chat rewards. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2209)
+ + Uses a compact chat message of rewards gained from Add-ons/Experiments.
++ Added Personal Compactor/Deletor Overlay. - Empa (https://github.com/hannibal002/SkyHanni/pull/1869)
+ + Shows what items are currently inside the personal compactor/deletor, and whether the accessory is turned on or off.
++ Added Accessory magical power display as stack size. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2243)
+ + Only works inside the Accessory Bag and Auction House.
+
+#### Chat Features
+
++ Added `/shcolors` command. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2216)
+ + Prints a list of all Minecraft color and formatting codes in chat.
++ Added Remind command. - ThatGravyBoat & Zickles (https://github.com/hannibal002/SkyHanni/pull/1708)
+ + Use `/shremind ` to set reminders for your future self.
+
+#### Command Features
+
++ Added Reverse Party Leader. - Jordyrat (https://github.com/hannibal002/SkyHanni/pull/1712)
+ + Reverse party transfer via clickable chat message or via the `/rpt` command.
+ + Configurable party chat message after transferring.
++ Added /shtps command. - saga (https://github.com/hannibal002/SkyHanni/pull/1961)
+ + Informs in chat about the server's ticks per second (TPS).
++ Added command `/shedittracker -
`. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2448)
+ + Changes the tracked item amount for Diana, Fishing, Pest, Excavator, and Slayer Item Trackers.
+ + Use a negative amount to remove items.
+
+#### Crimson Isle Features
+
++ Added Crimson Isle Miniboss Respawn Timer. - Empa (https://github.com/hannibal002/SkyHanni/pull/2042)
+
+#### Combat Features
+
++ Added Arachne Kill Timer. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/1993)
+ + Shows how long it took to kill Arachne when the fight ends.
+
+#### Misc Features
+
++ Added a notification when you join a server you have joined previously. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2046)
++ Added warning when Non-God Pot effects are about to expire. - saga (https://github.com/hannibal002/SkyHanni/pull/2004)
+ + Sends a title and plays a sound at a customizable time before the effect expires.
+
+### Improvements
+
+#### Mining Improvements
+
++ Now allows modifying the Mineshaft spawn message without requiring the Pity Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/2308)
++ Added reset command for Fossil Excavator Profit Tracker. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2408)
++ Added current session and total Mineshaft count to the Mineshaft Pity Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/2310)
++ Some icons in the Mining Event Display now use the texture of the item they represent. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2425)
+ + E.g., Raffle Event -> Raffle Ticket.
++ Changed how Powder Tracker works to account for powder buffs. - Jordyrat + Empa (https://github.com/hannibal002/SkyHanni/pull/2394)
+
+#### Diana Improvements
+
++ Added /warp stonks to the Burrow Warp Helper. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2399)
+
+#### Scoreboard Improvements
+
++ Added an option to display the SkyBlock time in 24-hour format in the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
++ Added an option to hide coins earned/lost in the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
++ Added margin customization to the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
++ Added an opacity slider for the Custom Background Image in the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
++ Added an Undo message when auto alignment inCustom Scoreboard gets disabled. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2402)
++ Added an option to show SkyBlock time on the custom scoreboard rounded to 10 minutes, similar to the vanilla scoreboard. - Luna (https://github.com/hannibal002/SkyHanni/pull/2443)
+
+#### Inventory Improvements
+
++ Added a toggle for displaying SkyHanni User Luck in Skyblock Stats. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2357)
++ Improved the clicked block highlight feature. - Vahvl & j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2166)
+ + Added extensive customization options for colors, locked chest detection, and more.
++ Improved Enchant Parsing compatibility with other mods. - Vixid (https://github.com/hannibal002/SkyHanni/pull/1717)
+ + Now works with SBA's Convert Roman Numerals feature.
+ + Now works with NEU's inventories, such as `/pv` and storage overlay.
++ Added exceptions to the enchant parser. - Vixid (https://github.com/hannibal002/SkyHanni/pull/2254)
+ + Stonk and non-mining tools with Efficiency 5 use the maximum enchant color.
++ Added a short cooldown between Experimentation Table Guardian Pet chat warnings. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2459)
+
+#### Chat Improvements
+
++ Added support for changing the translation language when clicking on a chat message. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2403)
++ Added support for using the "/gfs - " command without specifying an amount. - saga (https://github.com/hannibal002/SkyHanni/pull/1927)
+ + The default value can be modified in the config.
++ Reworked the chat formatting for reminders and warnings across multiple features. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2428)
+ + They now all allow performing an action on click (e.g., warping you somewhere or opening a menu) and can be disabled in the config via shift-click or control-click on the chat message.
+ + Changed chat messages: Account/Profile Upgrade reminder, Hoppity Cake reminder, New Year Hoppity NPC reminder, New Year Cake reminder, City Project reminder, Garden Composter empty warning, CF Custom Goal reached message, Time Tower warning, CF upgrade warning.
++ Added more options to the Chat Hider feature. - not_a_cow (https://github.com/hannibal002/SkyHanni/pull/2348)
+ + Option to block parkour messages and teleport pad messages.
+
+#### Hoppity Improvements
+
++ Added config option to disable rarity in Compact Hoppity messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2383)
+
+#### Combat Improvements
+
++ Made the "Line to Arachne" width configurable. - azurejelly (https://github.com/hannibal002/SkyHanni/pull/2406)
++ Made the "Line to Miniboss" width configurable. - azurejelly (https://github.com/hannibal002/SkyHanni/pull/2406)
+
+#### Config Improvements
+
++ Improved the description of several config options. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2397)
++ Enabled more Dev Options to be changed via /shdefaultoptions. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2397)
+
+#### Garden Improvements
+
++ Improved incorrect Rancher Boots speed warning. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2413)
+ + Also allows clicking on the message to open the edit sign directly.
+ + Now warns only when actually farming and wearing Rancher Boots.
++ Changed suggested cactus speed to 464 again. - DarkDash (https://github.com/hannibal002/SkyHanni/pull/2424)
+ + Since the cactus knife now allows 500 max speed.
++ Made the waypoint to the middle of the plot for Pest Waypoint optional (off by default). - Luna (https://github.com/hannibal002/SkyHanni/pull/2469)
+
+#### Rift Improvements
+
++ Updated the description of the config for Enigma Soul Waypoints to help find the Rift Guide in the game. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2433)
+
+### Fixes
+
+#### DIana Fixes
+
++ Added a chat option to disable a Patcher setting that breaks SkyHanni's line from the center of the player to something else. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2387)
+ + E.g. Diana guesses, Slayer Mini Bosses, etc.
+
+#### Bingo Fixes
+
++ Fixed "show bingo rank number" feature toggle not functioning. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2445)
+
+#### Inventory Fixes
+
++ Fixed ItemCategory errors being sent even though we already have a fix. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2392)
++ Fixed AH item movements being detected by Item Trackers as new drops. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2380)
++ Fixed Pickup Log Display Entries not expiring while in GUIs. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2401)
++ Fixed the price of mana enchantments tiers 1-4 in Estimated Chest value. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2395)
++ Fixed Guardian Reminder Experimentation Table not working with autopet rules. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2430)
++ Fixed SkyHanni Global Scale affecting Custom Wardrobe. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2420)
++ Fixed /show displaying an incorrect number of enchants. - Vixid (https://github.com/hannibal002/SkyHanni/pull/2435)
++ Fixed Armor Hider hiding armor inside the Custom Wardrobe. - Empa (https://github.com/hannibal002/SkyHanni/pull/2449)
++ Fixed detection of El Dorado and Stray Hordes in the Chocolate Factory. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2450)
++ Fixed merged enchant books tooltip being overwritten. - Vixid (https://github.com/hannibal002/SkyHanni/pull/2454)
+
+#### Mining Fixes
+
++ Added new Pure Ores support. - Empa (https://github.com/hannibal002/SkyHanni/pull/2388)
++ Fixed a typo in corpse detection. - Maratons4 (https://github.com/hannibal002/SkyHanni/pull/2386)
++ Fixed a compatibility issue in Fossil Solver with resource packs like Hypixel+. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2409)
++ Fixed Powder for 10 Levels overflowing over the max level. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2422)
++ Fixed Glacite Tunnel Maps repeatedly showing the last commission goal even when there is no new commission. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2432)
++ Fixed Mineshaft Pity Counter not working with some ore types. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2436)
+
+#### Scoreboard Fixes
+
++ Fixed a Scoreboard Error during M7 Dragons. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2382)
++ Fixed Custom Scoreboard Background position being updated 1 frame after the scoreboard text. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
++ Fixed various Custom Scoreboard issues. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2402)
+ + Bank showing "null" during Island Switch.
+ + Alignment not being affected by outline thickness.
+ + Alignment with scales that are not 1.
+ + Line Spacing not working.
++ Fixed Custom Scoreboard Error when no repository exists. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2407)
++ Fixed rounded rectangles not working with a scale less than 1. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2404)
+ + E.g. Custom Scoreboard Background.
++ Fixed rare Custom Scoreboard errors. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2411)
++ Fixed Minister not resetting after the election closed. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2467)
++ Fixed Custom Scoreboard errors while doing Carnival activities. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2455)
++ Fixed Custom Scoreboard error while in a Dungeon Queue. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2441)
++ Fixed Custom Scoreboard Error during the Barry Protestors Quest. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2480)
+
+#### Hoppity Fixes
+
++ Fixed "Side Dish" and Milestone rabbits not sending a message when Compact Hoppity is enabled. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
++ Fixed rabbit rarity not appearing on compacted Duplicate rabbit messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2383)
++ Fixed "Highlight Requirement Rabbits" requiring "Hoppity Collection Stats" to function. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2398)
++ Fixed minor formatting issues with Hoppity Event Summary. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2416)
+
+#### Misc Fixes
+
++ Fixed Mayor Detection failing when Special Mayors are in office. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2389)
++ Updated Derpy's extra tax perk to use the correct name. - Luna (https://github.com/hannibal002/SkyHanni/pull/2393)
++ Fixed Christmas presents in the Hypixel lobby being visible outside of December. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2396)
++ Fixed the mod crashing on startup when the game directory path contains a "!". - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2427)
++ Fixed chat formatting for coin drops in profit trackers (Diana, Slayer, Fishing, Pest). - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2444)
++ Fixed Last Server Time Slider not functioning. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2440)
++ Fixed warning sounds being stuttered and cutting out. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2482)
++ Fixed an internal bug with `getItemName` that caused the `NEUInternalName.NONE has no name!` error messages to appear. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2471)
++ Fixed a typo in mob detection config. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2472)
+
+#### Chat Fixes
+
++ Fixed an error with Patcher Coordinate Detection from chat. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2410)
++ Fixed ultimate enchants not showing in `/show`. - Vixid (https://github.com/hannibal002/SkyHanni/pull/2465)
+
+#### Commands Fixes
+
++ Fixed the command `/shtps` printing nothing if the TPS display was currently showing nothing. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2446)
+
+#### Dungeon Fixes
+
++ Fixed disabled SkyHanni features causing compatibility issues with other Dungeon Party Finder mods. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2470)
++ Fixed dungeon completion detection. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2456)
+ + This also fixes the Croesus reroll detection breaking.
+
+#### Crimson Isle Fixes
+
++ Fixed the reputation helper not updating after a sack update. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2439)
++ Fixed Heavy Pearls sometimes not being highlighted. - Luna (https://github.com/hannibal002/SkyHanni/pull/2479)
+
+### Technical Details
+
++ Cleaned up IslandExceptions to improve readability. - walker (https://github.com/hannibal002/SkyHanni/pull/1560)
++ Added drawInsideRoundedRectOutline, drawInsideImage, and image Renderables. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
++ Added `HoppityAPI`, which should house event-specific features rather than `ChocolateFactoryAPI`. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
+ + Added an `Always Hoppity` dev config option, which will override `isHoppityEvent`.
++ Added `RabbitFoundEvent`, which will be fired when a chocolate rabbit is found from an egg, bought from Hoppity, or claimed from a milestone. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
++ Added `SIDE_DISH`, `CHOCOLATE_SHOP_MILESTONE`, `CHOCOLATE_FACTORY_MILESTONE`, and `BOUGHT` as `HoppityEggType` options. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
++ Added version to download source checker popup message. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2400)
++ Added "hex string to color int" method in ColorUtils. - nopo (https://github.com/hannibal002/SkyHanni/pull/2321)
++ Fixed Sound Errors in the console. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2431)
++ Added /shdebugscoreboard to monitor scoreboard changes. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2412)
+ + Prints the raw scoreboard lines in the console after each update, with the time since the last update.
++ Added split and dissolve features for the graph editor. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2466)
++ Added ghost position feature to the graph editor, useful for editing nodes that are in the air. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2461)
++ Added tags to the graph editor. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2464)
++ Changed graph node editor behavior. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2451)
+ + WASD node movement now reflects the player's look direction.
+ + Nodes are only deleted when they are selected/active.
+ + Allows creating nodes much closer to each other.
++ Replaced `List
>` with `List` in all SkyHanni trackers. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2474)
++ Rebased Hoppity config to be in its own package. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
++ Use on DelayedRun to add scheduled tasks. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2462)
++ Created a `ClickBlockType` enum and a `DungeonBlockClickEvent` class. - Ovi_1 (https://github.com/hannibal002/SkyHanni/pull/2478)
+ + Added a new enum for block types and a class to handle click events on dungeon blocks.
++ Updated `DungeonHighlightClickedBlocks` to use the new `DungeonBlockClickEvent` class. - Ovi_1 (https://github.com/hannibal002/SkyHanni/pull/2478)
++ Added fakeInventory, paddingContainer and drawInsideFixedSizedImage. - Empa (https://github.com/hannibal002/SkyHanni/pull/1869)
++ Changed RenderableTooltips to be more consistent with the vanilla ones. - Empa (https://github.com/hannibal002/SkyHanni/pull/1869)
++ Improved error logging for enchant parser. - Vixid (https://github.com/hannibal002/SkyHanni/pull/2454)
++ Added renderable search box. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2475)
+
+## Version 0.26
### New Features
@@ -16,7 +310,7 @@
azurejelly (https://github.com/hannibal002/SkyHanni/pull/1888)
+ Added In-Water Display. - Stella (https://github.com/hannibal002/SkyHanni/pull/1892)
+ Useful when using a Prismarine Blade in Stranded Mode.
-+ Added toggle for compacting Garden visitor summary messages. - DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/2026)
++ Added toggle for compacting Garden visitor summary messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2026)
+ Add Armor Stack Display. - Xupie (https://github.com/hannibal002/SkyHanni/pull/1811)
+ Display the number of stacks on armor pieces like Crimson, Terror etc.
+ Add a line to Nukekebi Skull. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2148)
@@ -150,7 +444,7 @@
+ Highly customizable: Colors, display sizes.
+ Estimated Price Integration.
+ Favorite slots; option to only display favorite slots.
-+ Added toggle for compacting Garden visitor summary messages. - DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/2026)
++ Added toggle for compacting Garden visitor summary messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2026)
+ Added SkyHanni User Luck to the stats breakdown. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/1288)
+ This can be viewed in /sbmenu, /equipment, and the Misc Stats submenu.
+ Added a source download verification checker. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/1914)
@@ -180,13 +474,14 @@
+ Also shortened the default display.
+ Minor GUI improvements in /ff. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/873)
+ Displays in /ff the fortune from bestiary and armor stats for enchantments and gemstones. - maxime-bodifee (https://github.com/hannibal002/SkyHanni/pull/1861)
-+ Allow clicking on the Craftable! text in the shopping list to open the "view recipe" menu. - DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/2075)
++ Allow clicking on the Craftable! text in the shopping list to open the "view recipe" menu. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2075)
+ Rename Mushroom Tier to Mushroom Milestone in Mooshroom Cow Perk. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2159)
+ Add Fine Flour to the /ff menu. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2194)
+ Added a "Compact" option to the Rancher Boots Optimal Speed GUI. - Empa (https://github.com/hannibal002/SkyHanni/pull/2137)
+ The Rancher Boots Optimal Speed GUI now highlights the crop corresponding to the last held tool. - Empa (https://github.com/hannibal002/SkyHanni/pull/2137)
+ Added Slug pet to the FF Guide. - maxime-bodifee (https://github.com/hannibal002/SkyHanni/pull/2249)
+ Added Copper dye to the list of blocked visitor rewards. - not-a-cow (https://github.com/hannibal002/SkyHanni/pull/2329)
++ Added Copper Dye to Visitor Drop Statistics. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2346)
#### Hoppity Event Improvements
@@ -196,7 +491,7 @@
+ Hoppity Collection Stats are now persistent. - appable (https://github.com/hannibal002/SkyHanni/pull/1836)
+ No longer reset on profile swap or game restart.
+ Merged duplicate times in compacted Hoppity's messages. -
- DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/1887)
+ Daveed (https://github.com/hannibal002/SkyHanni/pull/1887)
+ Option to show Hoppity Eggs timer outside of SkyBlock. -
maxime-bodifee (https://github.com/hannibal002/SkyHanni/pull/1926)
+ Adjusted and added Rabbit Uncle & Dog Keybinds. - raven (https://github.com/hannibal002/SkyHanni/pull/1907)
@@ -213,6 +508,7 @@
+ Changed click-to-warp toggle to also work for unclaimed eggs display. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2211)
+ When Chocolate Factory prestige is maxed, the display shows time until the next max milestone instead. - seraid (https://github.com/hannibal002/SkyHanni/pull/2226)
+ Added an option to choose the color of Hoppity Egg Waypoints. - jani (https://github.com/hannibal002/SkyHanni/pull/2259)
++ Added rabbit rarity to Compact Hoppity messages. - not_a_cow (https://github.com/hannibal002/SkyHanni/pull/2364)
#### Mining Improvements
@@ -321,6 +617,7 @@
+ Added the current Minister's name and perks to the Mayor Display on the Custom Scoreboard. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2313)
+ Hide repo errors in chat. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2338)
+ Errors will be displayed directly in the GUI when necessary, making them less intrusive.
++ Added a "Mods Folder" button to the NEU error message. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2362)
### Fixes
@@ -361,6 +658,7 @@
+ Fixed an issue where TunnelMaps could stop functioning. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2277)
+ Fixed blocks mined by Efficient Miner not being detected in the Pity Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/2307)
+ Fixed empty lines not being blocked by the Powder Mining filter. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2309)
++ Fixed a rare crash occurring on a mining island. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2354)
#### Garden Fixes
@@ -384,7 +682,7 @@
+ Fixed Fine Flour exportation not getting detected from chat messages for use in FF Guide. - Luna (https://github.com/hannibal002/SkyHanni/pull/2215)
+ Fixed "Craftable!" text not opening the recipe. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2219)
+ Fixed favorite pets not being detected by FF Guide. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2202)
-+ Fixed farming weight estimate being much too large. - DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/2230)
++ Fixed farming weight estimate being much too large. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2230)
+ Fixed Jacob's Contest FF needed display not showing. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2262)
+ Fixed Dicer Tracker Reset Button misalignment. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2248)
+ Fixed errors when selecting Fine Flour with the Sprayonator. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2251)
@@ -393,6 +691,9 @@
+ Fixed visitor shopping list with AH items. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2297)
+ Fixed Zorro's Cape not working with Green Thumb in the FF stats breakdown in item lore. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2299)
+ Fixed Composter Overlay calculating incorrectly while Composter Display is disabled. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2294)
++ Fixed own garden not being detected when swapping islands from a guesting server. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2355)
++ Fixed a typo in the Farming Weight Display error message. - aphased (https://github.com/hannibal002/SkyHanni/pull/2376)
++ Fixed Finnegan still having the Farming Simulator perk and missing the Pest Eradicator perk. - Luna (https://github.com/hannibal002/SkyHanni/pull/2359)
#### Chocolate Factory & Hoppity Hunt Fixes
@@ -406,7 +707,7 @@
appable (https://github.com/hannibal002/SkyHanni/pull/1925)
+ Fixed incorrect API name conversion for Fish the Rabbit. - appable (https://github.com/hannibal002/SkyHanni/pull/1975)
+ Fixed stray rabbit sound not playing for the Golden Rabbit. - HiZe (https://github.com/hannibal002/SkyHanni/pull/1913)
-+ Fixed compact chat sometimes breaking when obtaining legendary or higher tier rabbits. - DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/2041)
++ Fixed compact chat sometimes breaking when obtaining legendary or higher tier rabbits. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2041)
+ Fixed highlight missing requirements even when Hoppity Rabbit is already found. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2101)
+ Fixed Compact Hoppity Chat not working with rabbits purchased from the Hoppity NPC. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2100)
+ Fixed a rare case where golden strays caused incorrect duplicate detection during Compact Chat for Hoppity's. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2090)
@@ -425,6 +726,8 @@
+ Fixed stray rabbit production time tooltip display. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2239)
+ Fixed Hoppity custom goals being off by one level. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2280)
+ Fixed Custom Reminder not working in the Chocolate Shop. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2334)
++ Fixed a warning about space in the Chocolate Factory appearing even when the barn was already large enough to store all rabbits. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2356)
++ Fixed the Hoppity Menu not being detected when it only had one page. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2368)
#### Custom Scoreboard Fixes
@@ -446,6 +749,8 @@
+ Fixed Scoreboard Reset not updating the draggable list. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2252)
+ Fixed a scoreboard error message when a Hypixel bug shows a negative number of magma cubes remaining in the scoreboard. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2267)
+ Fixed Custom Scoreboard Error when having more than 1000 medals. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2319)
++ Fixed a Custom Scoreboard error during the Winter Event. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2345)
++ Fixed getting mayor/minister information for Custom Scoreboard. - Luna (https://github.com/hannibal002/SkyHanni/pull/2343)
#### Dungeon Fixes
@@ -474,6 +779,11 @@
+ Fixed a rare double hook detection issue. - appable (https://github.com/hannibal002/SkyHanni/pull/2125)
+ Fixed Commission Mob Highlighter incorrectly highlighting Deep Sea Protectors as Automatons. - Luna (https://github.com/hannibal002/SkyHanni/pull/2130)
+ Fixed Squid Pet counting towards the Fishing Timer. - Empa (https://github.com/hannibal002/SkyHanni/pull/1960)
++ Fixed an error during fishing. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2350)
++ Fixed error messages appearing while fishing. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2370)
++ Fixed obfuscated trophy fish names being unreadable in the Fishing Profit Tracker. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2363)
++ Fixed item names appearing in incorrect spots while holding a fishing rod. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2371)
++ Fixed the fishing timer being incorrect for some sea creatures. - Empa (https://github.com/hannibal002/SkyHanni/pull/2365)
#### Performance Fixes
@@ -490,6 +800,7 @@
+ Fixed rift slayer warning showing while hitting Oubliette Guard. -
hannibal2 (https://github.com/hannibal002/SkyHanni/pull/1950)
++ Fixed an error when killing Splatter Cruxes in the Rift. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2349)
#### Inventory Fixes
@@ -518,6 +829,10 @@
+ Fixed the incorrect "This item has no attributes" message appearing in the Attribute Fusion menu for some items. - Luna (https://github.com/hannibal002/SkyHanni/pull/2304)
+ Fixed shift-clicking items in GUIs triggering the Item Pickup Log. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2305)
+ Fixed the Master Mode Dungeon stack size display in the party finder menu. - fazfoxy (https://github.com/hannibal002/SkyHanni/pull/2316)
++ Fixed the Item Pickup Log detecting "Magical Map" in dungeons when using Skytils. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2351)
++ Fixed Attribute Shards not displaying their names in the Chest Value feature. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2363)
++ Fixed Bazaar item movement being counted towards Item Trackers. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2366)
++ Fixed the Item Pickup Log showing incorrect icons for some items when collected via sacks. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2373)
#### Combat Fixes
@@ -525,6 +840,7 @@
+ Fixed area mini boss spawn durations below 1 second not displaying. - Empa (https://github.com/hannibal002/SkyHanni/pull/2110)
+ Fixed summons death messages getting blocked even when the feature is disabled. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2132)
+ Fixed slayer quest warning sometimes showing incorrectly. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2232)
++ Fixed the "wrong slayer" alert being shown when attacking Bladesoul. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2367)
#### Chat Fixes
@@ -570,6 +886,13 @@
+ Fixed Contributor name tag flickering when using Patcher. - nea (https://github.com/hannibal002/SkyHanni/pull/2296)
+ Fixed Discord Rich Presence dynamic fallback. - NetheriteMiner (https://github.com/hannibal002/SkyHanni/pull/2279)
+ Placeholder messages that should not be visible will no longer appear.
++ Fixed pet detection with newer NEU versions. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2358)
++ Fixed Item Trackers displaying the incorrect NPC price. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2352)
++ Fixed players with similar names to friends being incorrectly marked as friends in the tab list. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2353)
++ Fixed color codes in Discord RPC pet status. - NetheriteMiner (https://github.com/hannibal002/SkyHanni/pull/2344)
++ Disabled the SkyHanni GUI's hover function while in other mods' screens. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2374)
++ Fixed the Trapper Helper checking for the wrong Finnegan perk. - Luna (https://github.com/hannibal002/SkyHanni/pull/2359)
++ Fixed items with no NPC sell price being counted as -1 coins instead of 0 coins. - Luna (https://github.com/hannibal002/SkyHanni/pull/2360)
### Technical Details
diff --git a/docs/DISCORD_FAQ.md b/docs/DISCORD_FAQ.md
index 010894e8295f..72c1aefb31a7 100644
--- a/docs/DISCORD_FAQ.md
+++ b/docs/DISCORD_FAQ.md
@@ -57,11 +57,17 @@ _Frequently Asked Questions_
> **12: Why can I still see the normal Scoreboard when using Custom Scoreboard?**
> Most of the time, this is a mod conflict.
> If you are using [Sidebar Mod](https://github.com/Alexdoru/SidebarMod), please remove this mod.
-> If you are using [VanillaHUD](https://modrinth.com/mod/vanillahud), please update to 2.2.8 or newer to resolve this issue.
+> If you are using [VanillaHUD](https://modrinth.com/mod/vanillahud), please update to 2.2.9 or newer to resolve this issue.
> If you are using [Apec](https://github.com/BananaFructa/Apec/) and want to remove their Scoreboard, you need to remove Apec since they don't have an option to disable their Scoreboard.
+> If you are using [Odin](https://github.com/odtheking/Odin), disable their "Sidebar".
> If you are using [Patcher](https://sk1er.club/mods/patcher) or [PolyPatcher](https://modrinth.com/mod/patcher) and the vanilla scoreboard is flickering, disable the "HUD Caching" option.
> If you don't use any of these mods, make sure the option to "Hide Vanilla Scoreboard" is actually enabled.
+> **13: Why doesn't the burrow warp key and line prioritize my guess waypoint when there are known burrows nearby?**
+> This is intended behavior. SkyHanni prioritizes the closest known burrow or guess waypoint, not necessarily the guess waypoint itself. Focusing on the closest point, even if it's a known burrow and not your guess, is faster and leads to a higher "burrows dug over time" rate, meaning more mobs, more inquisitors, more money and faster milestones.
+> While interrupting your current chain to focus on a nearby burrow might feel counterintuitive, it ultimately benefits you in the long run.
+> The existing chain isn't reset, and you gain the advantage of a higher burrow digging rate.
-*This FAQ was last updated on August 10th, 2024.
+
+*This FAQ was last updated on August 26th, 2024.
If you believe there's something that should be added to this list, please tell us, so we can add it.*
diff --git a/docs/FEATURES.md b/docs/FEATURES.md
index dd4e6ea41bc0..5ae7ac05a2be 100644
--- a/docs/FEATURES.md
+++ b/docs/FEATURES.md
@@ -110,6 +110,11 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Dungeon hub race waypoints. - seraid (https://github.com/hannibal002/SkyHanni/pull/1471)
+ Only works for Nothing; No return races.
+ Added the ability to hide solo class, solo class stats and fairy dialogue chat messages in Dungeons. - raven (https://github.com/hannibal002/SkyHanni/pull/1702)
++ Added highlight for starred dungeon mobs. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1558)
++ Added highlight for Fel skulls. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1558)
+ + Optionally draws a line to them as well.
++ Added a Secret Chime for Dungeons with adjustable pitch and sound. - Ovi_1 (https://github.com/hannibal002/SkyHanni/pull/2478)
+ + The sound and pitch of chimes in dungeons are customizable.
@@ -529,6 +534,8 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ "Line to Arachne" setting, just like with slayer mini bosses. - azurejelly (https://github.com/hannibal002/SkyHanni/pull/1888)
+ Countdown for Arachne spawn. - Cad
+ Supports quick spawns.
++ Added Arachne Kill Timer. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/1993)
+ + Shows how long it took to kill Arachne when the fight ends.
+ Option to hide the vanilla particles around enderman
@@ -732,9 +739,13 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Checks if you have enough materials to craft the items and depending on that shows the button or not.
+ Overflow Garden crop milestones. - Luna & HiZe (https://github.com/hannibal002/SkyHanni/pull/997)
+ New "Craftable!" message when Visitor Items Needed are craftable. - Paloys (https://github.com/hannibal002/SkyHanni/pull/1891)
-+ Added toggle for compacting Garden visitor summary messages. - DavidArthurCole (https://github.com/hannibal002/SkyHanni/pull/2026)
++ Added toggle for compacting Garden visitor summary messages. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2026)
+ Added Farming Personal Best FF Gain. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2332)
+ Displays in chat how much additional FF you earn from the farming contest personal best bonus after beating your previous record.
++ Added No Pests Chat Filter. - saga (https://github.com/hannibal002/SkyHanni/pull/1957)
+ + Removed the chat message "There are no Pests on your Garden!".
++ Added No Pests Title. - saga (https://github.com/hannibal002/SkyHanni/pull/1957)
+ + Shows a title when you use the Pest Tracker without any pests to clear.
@@ -816,6 +827,10 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Punchcard Artifact Highlighter & Overlay. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/1089)
+ Highlights unpunched players in the Rift.
+ Added Splatter Hearts Highlight for the Rift. - Empa (https://github.com/hannibal002/SkyHanni/pull/2318)
++ Added Motes per Session. - Empa (https://github.com/hannibal002/SkyHanni/pull/2323)
++ Added Crafting Room Helper. - HiZe (https://github.com/hannibal002/SkyHanni/pull/2178)
+ + Shows a holographic mob at the location where the mob is present in the real room inside the Mirrorverse in Rift.
++ Added Rift Time Real-Time Nametag Format. - Empa (https://github.com/hannibal002/SkyHanni/pull/2015)
@@ -876,6 +891,7 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Added powder cost for 10 levels.
+ Mineshaft Pity Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/1655)
+ Displays information about Mineshaft spawns, your progress towards a Pity Mineshaft, and more.
++ Added a "Get from Sack" button in the forge recipe menu to retrieve ingredients. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2106)
@@ -962,6 +978,10 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Add reminder that the Time Tower is expired and has extra charges available. - appable (https://github.com/hannibal002/SkyHanni/pull/2133)
+ Replaces the previous Time Tower Reminder feature.
+ Added a tracker for stray rabbits caught in the Chocolate Factory. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2210)
++ Added Hoppity Hunt event summary. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
+ + Use /shhoppitystats for live stats.
++ Added optional warning when Hoppity calls you with a rabbit to sell. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
++ Added hotkey for picking up Abiphone calls from Hoppity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
@@ -993,6 +1013,15 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Can be used like `/shupdate ` to download updates from a specific update stream.
+ /shignore. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/1469)
+ This lets you block users from running party commands.
++ Added `/shcolors` command. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2216)
+ + Prints a list of all Minecraft color and formatting codes in chat.
++ Added Remind command. - ThatGravyBoat & Zickles (https://github.com/hannibal002/SkyHanni/pull/1708)
+ + Use `/shremind ` to set reminders for your future self.
++ Added /shtps command. - saga (https://github.com/hannibal002/SkyHanni/pull/1961)
+ + Informs in chat about the server's ticks per second (TPS).
++ Added command `/shedittracker -
`. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2448)
+ + Changes the tracked item amount for Diana, Fishing, Pest, Excavator, and Slayer Item Trackers.
+ + Use a negative amount to remove items.
@@ -1021,6 +1050,8 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Option to hide all damage splashes, from anywhere in SkyBlock.
+ Hide armor or just helmet of other player or yourself
+ Display the active non-god potion effects.
++ Added warning when Non-God Pot effects are about to expire. - saga (https://github.com/hannibal002/SkyHanni/pull/2004)
+ + Sends a title and plays a sound at a customizable time before the effect expires.
+ Wishing compass uses amount display.
+ Brewing Stand Overlay.
+ Crimson Isle Reputation Helper.
@@ -1039,6 +1070,7 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Dojo Rank Display. - HiZe
+ Display your rank, score, actual belt and points needed for the next belt in the Challenges inventory on the
Crimson Isles.
++ Added Crimson Isle Miniboss Respawn Timer. - Empa (https://github.com/hannibal002/SkyHanni/pull/2042)
+ Matriarch Helper. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1385)
+ Highlights the Heavy Pearls.
+ Draws a line to the Heavy Pearls.
@@ -1230,8 +1262,14 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ A ton of settings.
+ Added Chunked Stats as a draggable list to the Custom Scoreboard. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/1341)
+ Select and prioritize the order of the stats you want to see.
+ + Added Soulflow to the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
+ + Requires Soulflow to be enabled in Hypixel settings: /tab -> Profile Widget -> Show Soulflow.
+ No Bits Available Warning. - Empa (https://github.com/hannibal002/SkyHanni/pull/1286)
+ Warns when you run out of available bits to generate.
++ Added Display for Bits on Cookie buy. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2265)
+ + Shows the Bits you would gain.
+ + Can show the change for the available Bits.
+ + Also shows the time more clearly (or adds it if not present).
+ Link from HUD elements to config options. - nea (https://github.com/hannibal002/SkyHanni/pull/1383)
+ Simply right-click a HUD element in the HUD editor to jump to its associated options.
+ Does not yet work on every GUI element. Wait for the missing elements in the following betas.
@@ -1244,6 +1282,9 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Option to highlight Zealots holding Chests in a different color. - Luna (https://github.com/hannibal002/SkyHanni/pull/1347)
+ Allow party members to request allinvite to be turned on. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/1464)
+ Say !allinv in party chat for them to enable all invite.
++ Added Reverse Party Leader. - Jordyrat (https://github.com/hannibal002/SkyHanni/pull/1712)
+ + Reverse party transfer via clickable chat message or via the `/rpt` command.
+ + Configurable party chat message after transferring.
+ Added Hide Far Entities. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/1064)
+ Can perhaps increase FPS for some users by 5% to 150%.
+ Options to change the distance and number of mobs to always show.
@@ -1257,6 +1298,11 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Editable Hotbar. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1903)
+ Allows for moving and scaling in the SkyHanni GUI editor.
+ ULTRA RARE Book Notification when doing the Experiment Table. - raven (https://github.com/hannibal002/SkyHanni/pull/1738)
++ Added a warning when opening the Experimentation Table without a Guardian pet. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2127)
++ Added Enchanting Experience as Stack Size in Experimentation Table. - saga (https://github.com/hannibal002/SkyHanni/pull/1988)
+ + Added to the Item Number list.
++ Added Compact Experimentation Table chat rewards. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2209)
+ + Uses a compact chat message of rewards gained from Add-ons/Experiments.
+ In-Water Display. - Stella (https://github.com/hannibal002/SkyHanni/pull/1892)
+ Useful when using a Prismarine Blade in Stranded Mode.
+ Added Beacon Power Display. - Empa (https://github.com/hannibal002/SkyHanni/pull/1901)
@@ -1279,6 +1325,16 @@ Use `/sh` or `/skyhanni` to open the SkyHanni config in game.
+ Also punishes players who don't read patch notes.
+ Added Crown of Avarice counter. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2229)
+ Added a title notification for when the Thunder Bottle is fully charged. - raven (https://github.com/hannibal002/SkyHanni/pull/2234)
++ Added Editable XP Bar. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1944)
+ + Enabled moving and scaling of the XP bar in the SkyHanni GUI Editor.
++ Show dye hex code as the actual color in the item lore. - nopo (https://github.com/hannibal002/SkyHanni/pull/2321)
++ Added a notification when you join a server you have joined previously. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2046)
++ Added Personal Compactor/Deletor Overlay. - Empa (https://github.com/hannibal002/SkyHanni/pull/1869)
+ + Shows what items are currently inside the personal compactor/deletor, and whether the accessory is turned on or off.
++ Added Display Tab Widgets. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1276)
+ + Allows the display of information from the Tab (e.g., Bestiary info).
++ Added Accessory magical power display as stack size. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2243)
+ + Only works inside the Accessory Bag and Auction House.
diff --git a/docs/gradle-settings.png b/docs/gradle-settings.png
new file mode 100644
index 000000000000..62b1382cd13c
Binary files /dev/null and b/docs/gradle-settings.png differ
diff --git a/docs/gradle-tab.jpg b/docs/gradle-tab.jpg
new file mode 100644
index 000000000000..4667cdaa987f
Binary files /dev/null and b/docs/gradle-tab.jpg differ
diff --git a/docs/minecraft-client.webp b/docs/minecraft-client.webp
new file mode 100644
index 000000000000..a243632cbe68
Binary files /dev/null and b/docs/minecraft-client.webp differ
diff --git a/docs/run-configuration-settings.avif b/docs/run-configuration-settings.avif
new file mode 100644
index 000000000000..4c25607aeb74
Binary files /dev/null and b/docs/run-configuration-settings.avif differ
diff --git a/root.gradle.kts b/root.gradle.kts
index b74af223fd85..5fbee7047912 100644
--- a/root.gradle.kts
+++ b/root.gradle.kts
@@ -12,7 +12,7 @@ plugins {
allprojects {
group = "at.hannibal2.skyhanni"
- version = "0.26.Beta.23"
+ version = "0.27.Beta.6"
}
preprocess {
diff --git a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
index eb87426d4d2d..c0723cf48dec 100644
--- a/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
+++ b/src/main/java/at/hannibal2/skyhanni/SkyHanniMod.kt
@@ -42,7 +42,7 @@ import org.apache.logging.log4j.Logger
clientSideOnly = true,
useMetadata = true,
guiFactory = "at.hannibal2.skyhanni.config.ConfigGuiForgeInterop",
- version = "0.26.Beta.23",
+ version = "0.27.Beta.6",
)
class SkyHanniMod {
diff --git a/src/main/java/at/hannibal2/skyhanni/api/CollectionAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/CollectionAPI.kt
index 3e7e1f7bf964..1f5adacf70ab 100644
--- a/src/main/java/at/hannibal2/skyhanni/api/CollectionAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/api/CollectionAPI.kt
@@ -1,5 +1,6 @@
package at.hannibal2.skyhanni.api
+import at.hannibal2.skyhanni.data.ItemAddManager
import at.hannibal2.skyhanni.events.CollectionUpdateEvent
import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.events.ItemAddEvent
@@ -88,6 +89,7 @@ object CollectionAPI {
@SubscribeEvent
fun onItemAdd(event: ItemAddEvent) {
+ if (event.source == ItemAddManager.Source.COMMAND) return
val internalName = event.internalName
val amount = NEUItems.getPrimitiveMultiplier(internalName).amount
if (amount > 1) return
diff --git a/src/main/java/at/hannibal2/skyhanni/api/GetFromSackAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/GetFromSackAPI.kt
index 54517905a5cc..7407dd66053a 100644
--- a/src/main/java/at/hannibal2/skyhanni/api/GetFromSackAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/api/GetFromSackAPI.kt
@@ -148,7 +148,7 @@ object GetFromSackAPI {
when (result) {
CommandResult.VALID -> getFromSack(stack ?: return)
- CommandResult.WRONG_ARGUMENT -> ChatUtils.userError("Missing arguments! Usage: /getfromsacks ")
+ CommandResult.WRONG_ARGUMENT -> ChatUtils.userError("Missing arguments! Usage: /getfromsacks [amount]")
CommandResult.WRONG_IDENTIFIER -> ChatUtils.userError("Couldn't find an item with this name or identifier!")
CommandResult.WRONG_AMOUNT -> ChatUtils.userError("Invalid amount!")
CommandResult.INTERNAL_ERROR -> {}
@@ -168,20 +168,27 @@ object GetFromSackAPI {
)
private fun commandValidator(args: List): Pair {
- if (args.size <= 1) {
- return CommandResult.WRONG_ARGUMENT to null
- }
+ if (args.isEmpty()) return CommandResult.WRONG_ARGUMENT to null
+
+ // The last parameter could be "2*3". This does not support ending with ")", but it is good enough
+ val argsNull = !args.last().last().isDigit()
+ val arguments = if (argsNull) {
+ if (!config.defaultGFS) return CommandResult.WRONG_ARGUMENT to null
+
+ args + config.defaultAmountGFS.toString()
+ } else args
- var amountString = args.last()
+ var amountString = arguments.last()
amountString = NEUCalculator.calculateOrNull(amountString)?.toString() ?: amountString
if (!amountString.isDouble()) return CommandResult.WRONG_AMOUNT to null
- val itemString = args.dropLast(1).joinToString(" ").uppercase().replace(':', '-')
+ val itemString = arguments.dropLast(1).joinToString(" ").uppercase().replace(':', '-')
+ val replacedString = itemString.replace("_"," ")
val item = when {
SackAPI.sackListInternalNames.contains(itemString) -> itemString.asInternalName()
- SackAPI.sackListNames.contains(itemString) -> NEUInternalName.fromItemNameOrNull(itemString) ?: run {
+ SackAPI.sackListNames.contains(replacedString) -> NEUInternalName.fromItemNameOrNull(replacedString) ?: run {
ErrorManager.logErrorStateWithData(
"Couldn't resolve item name",
"Query failed",
diff --git a/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt b/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt
index 39305a012af1..97e041a89475 100644
--- a/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/api/HotmAPI.kt
@@ -2,12 +2,14 @@ package at.hannibal2.skyhanni.api
import at.hannibal2.skyhanni.data.HotmData
import at.hannibal2.skyhanni.data.ProfileStorageData
+import at.hannibal2.skyhanni.events.mining.PowderGainEvent
+import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemCategory
import at.hannibal2.skyhanni.utils.ItemUtils.getItemCategoryOrNull
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.SkyBlockItemModifierUtils.getDrillUpgrades
-import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase
import at.hannibal2.skyhanni.utils.TimeLimitedCache
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraft.item.ItemStack
@@ -30,7 +32,7 @@ object HotmAPI {
}
} == true
- enum class Powder(val displayName: String, val color: String) {
+ enum class PowderType(val displayName: String, val color: String) {
MITHRIL("Mithril", "§2"),
GEMSTONE("Gemstone", "§d"),
GLACITE("Glacite", "§b"),
@@ -71,9 +73,11 @@ object HotmAPI {
}
/** Use when new powder gets collected*/
- fun gain(value: Long) {
- addTotal(value)
- addCurrent(value)
+ fun gain(difference: Long) {
+ ChatUtils.debug("Gained §a${difference.addSeparators()} §e${displayName} Powder")
+ addTotal(difference)
+ addCurrent(difference)
+ PowderGainEvent(this, difference).post()
}
fun reset() {
diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
index db70f7e89288..4ac3f7925ce5 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigManager.kt
@@ -2,6 +2,7 @@ package at.hannibal2.skyhanni.config
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.core.config.Position
+import at.hannibal2.skyhanni.config.core.config.PositionList
import at.hannibal2.skyhanni.data.jsonobjects.local.FriendsJson
import at.hannibal2.skyhanni.data.jsonobjects.local.JacobContestsJson
import at.hannibal2.skyhanni.data.jsonobjects.local.KnownFeaturesJson
@@ -39,7 +40,6 @@ import java.nio.file.StandardCopyOption
import java.util.EnumMap
import kotlin.concurrent.fixedRateTimer
import kotlin.reflect.KMutableProperty0
-import kotlin.reflect.KMutableProperty1
private fun GsonBuilder.registerIfBeta(create: TypeAdapterFactory): GsonBuilder {
return if (LorenzUtils.isBetaVersion()) {
@@ -66,8 +66,7 @@ class ConfigManager {
private fun setConfigHolder(type: ConfigFileType, value: Any) {
require(value.javaClass == type.clazz)
- @Suppress("UNCHECKED_CAST")
- (type.property as KMutableProperty0).set(value)
+ @Suppress("UNCHECKED_CAST") (type.property as KMutableProperty0).set(value)
(jsonHolder as MutableMap)[type] = value
}
@@ -96,8 +95,7 @@ class ConfigManager {
try {
findPositionLinks(features, mutableSetOf())
} catch (e: Exception) {
- if (LorenzEvent.isInGuardedEventHandler)
- throw e
+ if (LorenzEvent.isInGuardedEventHandler) throw e
}
}
@@ -125,7 +123,7 @@ class ConfigManager {
var missingConfigLink = false
for (field in obj.javaClass.fields) {
field.isAccessible = true
- if (field.type != Position::class.java) {
+ if (field.type != Position::class.java && field.type != PositionList::class.java) {
findPositionLinks(field.get(obj), slog)
continue
}
@@ -141,8 +139,13 @@ class ConfigManager {
}
continue
}
- val position = field.get(obj) as Position
- position.setLink(configLink)
+ if (field.type == Position::class.java) {
+ val position = field.get(obj) as Position
+ position.setLink(configLink)
+ } else if (field.type == PositionList::class.java) {
+ val list = field.get(obj) as PositionList
+ list.setLink(configLink)
+ }
}
if (missingConfigLink) {
println("")
@@ -245,7 +248,7 @@ class ConfigManager {
unit.toPath(),
file.toPath(),
StandardCopyOption.REPLACE_EXISTING,
- StandardCopyOption.ATOMIC_MOVE
+ StandardCopyOption.ATOMIC_MOVE,
)
} catch (e: AccessDeniedException) {
if (loop == 5) {
diff --git a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt
index 25cab54fbec5..c881cc578ba4 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/ConfigUpdaterMigrator.kt
@@ -12,7 +12,7 @@ import com.google.gson.JsonPrimitive
object ConfigUpdaterMigrator {
val logger = LorenzLogger("ConfigMigration")
- const val CONFIG_VERSION = 54
+ const val CONFIG_VERSION = 57
fun JsonElement.at(chain: List, init: Boolean): JsonElement? {
if (chain.isEmpty()) return this
if (this !is JsonObject) return null
diff --git a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
index 89d7bb9bbeda..edfdb2d7a235 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/commands/Commands.kt
@@ -10,11 +10,14 @@ import at.hannibal2.skyhanni.data.GardenCropMilestonesCommunityFix
import at.hannibal2.skyhanni.data.GuiEditManager
import at.hannibal2.skyhanni.data.PartyAPI
import at.hannibal2.skyhanni.data.SackAPI
+import at.hannibal2.skyhanni.data.ScoreboardData
import at.hannibal2.skyhanni.data.TitleManager
+import at.hannibal2.skyhanni.data.TrackerManager
import at.hannibal2.skyhanni.data.bazaar.HypixelBazaarFetcher
import at.hannibal2.skyhanni.features.bingo.card.BingoCardDisplay
import at.hannibal2.skyhanni.features.bingo.card.nextstephelper.BingoNextStepHelper
-import at.hannibal2.skyhanni.features.chat.Translator
+import at.hannibal2.skyhanni.features.chat.ColorFormattingHelper
+import at.hannibal2.skyhanni.features.chat.translation.Translator
import at.hannibal2.skyhanni.features.combat.endernodetracker.EnderNodeTracker
import at.hannibal2.skyhanni.features.combat.ghostcounter.GhostUtil
import at.hannibal2.skyhanni.features.commands.HelpCommand
@@ -30,6 +33,7 @@ import at.hannibal2.skyhanni.features.event.diana.InquisitorWaypointShare
import at.hannibal2.skyhanni.features.event.diana.MythologicalCreatureTracker
import at.hannibal2.skyhanni.features.event.hoppity.HoppityCollectionStats
import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggLocations
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEventSummary
import at.hannibal2.skyhanni.features.event.jerry.frozentreasure.FrozenTreasureTracker
import at.hannibal2.skyhanni.features.fishing.tracker.FishingProfitTracker
import at.hannibal2.skyhanni.features.fishing.tracker.SeaCreatureTracker
@@ -54,14 +58,17 @@ import at.hannibal2.skyhanni.features.garden.visitor.GardenVisitorDropStatistics
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryStrayTracker
import at.hannibal2.skyhanni.features.mining.KingTalismanHelper
import at.hannibal2.skyhanni.features.mining.MineshaftPityDisplay
+import at.hannibal2.skyhanni.features.mining.fossilexcavator.ExcavatorProfitTracker
import at.hannibal2.skyhanni.features.mining.powdertracker.PowderTracker
import at.hannibal2.skyhanni.features.minion.MinionFeatures
import at.hannibal2.skyhanni.features.misc.CollectionTracker
import at.hannibal2.skyhanni.features.misc.LockMouseLook
import at.hannibal2.skyhanni.features.misc.MarkedPlayerManager
+import at.hannibal2.skyhanni.features.misc.TpsCounter
import at.hannibal2.skyhanni.features.misc.discordrpc.DiscordRPCManager
import at.hannibal2.skyhanni.features.misc.limbo.LimboTimeTracker
import at.hannibal2.skyhanni.features.misc.massconfiguration.DefaultConfigFeatures
+import at.hannibal2.skyhanni.features.misc.reminders.ReminderManager
import at.hannibal2.skyhanni.features.misc.update.UpdateManager
import at.hannibal2.skyhanni.features.misc.visualwords.VisualWordGui
import at.hannibal2.skyhanni.features.rift.area.westvillage.VerminTracker
@@ -169,6 +176,7 @@ object Commands {
{ DefaultConfigFeatures.onCommand(it) },
DefaultConfigFeatures::onComplete,
)
+ registerCommand("shremind", "Set a reminder for yourself") { ReminderManager.command(it) }
registerCommand("shwords", "Opens the config list for modifying visual words") { openVisualWords() }
}
@@ -277,6 +285,10 @@ object Commands {
"shresetstrayrabbittracker",
"Resets the Stray Rabbit Tracker",
) { ChocolateFactoryStrayTracker.resetCommand() }
+ registerCommand(
+ "shresetexcavatortracker",
+ "Resets the Fossil Excavator Profit Tracker",
+ ) { ExcavatorProfitTracker.resetCommand() }
registerCommand(
"shfandomwiki",
"Searches the fandom wiki with SkyHanni's own method.",
@@ -327,7 +339,7 @@ object Commands {
) { LimboTimeTracker.printStats() }
registerCommand(
"shlanedetection",
- "Detect a farming lane in garden",
+ "Detect a farming lane in the Garden",
) { FarmingLaneCreator.commandLaneDetection() }
registerCommand(
"shignore",
@@ -337,6 +349,18 @@ object Commands {
"shtpinfested",
"Teleports you to the nearest infested plot",
) { PestFinder.teleportNearestInfestedPlot() }
+ registerCommand(
+ "shhoppitystats",
+ "Look up stats for a Hoppity's Event (by SkyBlock year).\nRun standalone for a list of years that have stats.",
+ ) { HoppityEventSummary.sendStatsMessage(it) }
+ registerCommand(
+ "shcolors",
+ "Prints a list of all Minecraft color & formatting codes in chat.",
+ ) { ColorFormattingHelper.printColorCodeList() }
+ registerCommand(
+ "shtps",
+ "Informs in chat about the server ticks per second (TPS)."
+ ) { TpsCounter.tpsCommand() }
}
private fun usersBugFix() {
@@ -415,6 +439,10 @@ object Commands {
"shresetpunchcard",
"Resets the Rift Punchcard Artifact player list.",
) { PunchcardHighlight.clearList() }
+ registerCommand(
+ "shedittracker",
+ "Changes the tracked item amount for Diana, Fishing, Pest, Excavator, and Slayer Item Trackers.",
+ ) { TrackerManager.commandEditTracker(it) }
}
private fun developersDebugFeatures() {
@@ -452,7 +480,14 @@ object Commands {
"shtestisland",
"Sets the current skyblock island for testing purposes.",
) { SkyBlockIslandTest.onCommand(it) }
- registerCommand("shdebugprice", "Debug different price sources for an item.") { ItemPriceUtils.debugItemPrice(it) }
+ registerCommand(
+ "shdebugprice",
+ "Debug different price sources for an item."
+ ) { ItemPriceUtils.debugItemPrice(it) }
+ registerCommand(
+ "shdebugscoreboard",
+ "Monitors the scoreboard changes: Prints the raw scoreboard lines in the console after each update, with time since last update.",
+ ) { ScoreboardData.toggleMonitor() }
}
private fun developersCodingHelp() {
@@ -582,6 +617,7 @@ object Commands {
registerCommand("pt", "Transfer the party to another party member") { PartyCommands.transfer(it) }
registerCommand("pp", "Promote a specific party member") { PartyCommands.promote(it) }
registerCommand("pd", "Disbands the party") { PartyCommands.disband() }
+ registerCommand("rpt", "Reverse transfer party to the previous leader") { PartyCommands.reverseTransfer() }
}
@JvmStatic
diff --git a/src/main/java/at/hannibal2/skyhanni/config/core/config/Position.java b/src/main/java/at/hannibal2/skyhanni/config/core/config/Position.java
index 140fbf0076c7..eb37131b120c 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/core/config/Position.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/core/config/Position.java
@@ -48,11 +48,18 @@ public class Position {
@Expose
private boolean centerY;
+ @Expose
+ private boolean ignoreCustomScale = false;
+
public transient Field linkField;
private boolean clicked = false;
public String internalName = null;
+ public Position() {
+ this(0, 0);
+ }
+
public Position(int x, int y) {
this(x, y, false, false);
}
@@ -90,7 +97,13 @@ public void set(Position other) {
this.center = other.isCenter();
}
+ public Position setIgnoreCustomScale(boolean ignoreCustomScale) {
+ this.ignoreCustomScale = ignoreCustomScale;
+ return this;
+ }
+
public float getEffectiveScale() {
+ if (ignoreCustomScale) return 1F;
return Math.max(Math.min(getScale() * SkyHanniMod.feature.gui.globalScale, 10F), 0.1F);
}
@@ -115,6 +128,11 @@ public int getRawY() {
return y;
}
+ public void moveTo(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
public void setClicked(boolean state) {
this.clicked = state;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/core/config/PositionList.kt b/src/main/java/at/hannibal2/skyhanni/config/core/config/PositionList.kt
new file mode 100644
index 000000000000..c33f542934f9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/core/config/PositionList.kt
@@ -0,0 +1,47 @@
+package at.hannibal2.skyhanni.config.core.config
+
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink
+
+class PositionList() : ArrayList(), MutableList {
+
+ constructor(init: Collection) : this() {
+ this.addAll(init)
+ }
+
+ constructor(size: Int) : this() {
+ this.addAll((0..): Boolean {
+ if (configLink != null) {
+ elements.forEach {
+ it.setLink(configLink!!)
+ }
+ }
+ return super.addAll(elements)
+ }
+
+ override fun set(index: Int, element: Position): Position {
+ if (configLink != null) {
+ element.setLink(configLink!!)
+ }
+ return super.set(index, element)
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/enums/OutsideSbFeature.kt b/src/main/java/at/hannibal2/skyhanni/config/enums/OutsideSbFeature.kt
index e334785c556e..9180273aab78 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/enums/OutsideSbFeature.kt
+++ b/src/main/java/at/hannibal2/skyhanni/config/enums/OutsideSbFeature.kt
@@ -18,10 +18,11 @@ enum class OutsideSbFeature(private val displayName: String) {
FOLLOWING_LINE("Following Line"),
ARROW_TRAIL("Arrow Trail"),
HIGHLIGHT_PARTY_MEMBERS("Highlight Party Members"),
- MOVEMENT_SPEED("Movement Speed");
+ MOVEMENT_SPEED("Movement Speed"),
+ CUSTOM_SCOREBOARD("Custom Scoreboard (only on Hypixel)"),
+ ;
override fun toString() = displayName
- fun isSelected() =
- Minecraft.getMinecraft().thePlayer != null && SkyHanniMod.feature.misc.showOutsideSB.contains(this)
+ fun isSelected() = Minecraft.getMinecraft().thePlayer != null && SkyHanniMod.feature.misc.showOutsideSB.get().contains(this)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/ChatConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/ChatConfig.java
index f9e64f1e07d4..53f7aac94df8 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/chat/ChatConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/ChatConfig.java
@@ -3,6 +3,7 @@
import at.hannibal2.skyhanni.config.FeatureToggle;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.Accordion;
+import io.github.notenoughupdates.moulconfig.annotations.Category;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind;
@@ -88,6 +89,12 @@ public String toString() {
@FeatureToggle
public boolean compactBestiaryMessage = true;
+ @Expose
+ @ConfigOption(name = "Compact Enchanting Rewards", desc = "Compact the rewards gained from Add-ons and Experiments in Experimentation Table, only showing additional information when hovering.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean compactExperimentationTable = false;
+
@Expose
@ConfigOption(name = "Arachne Hider", desc = "Hide chat messages about the Arachne Fight while outside of §eArachne's Sanctuary§7.")
@ConfigEditorBoolean
@@ -104,16 +111,9 @@ public String toString() {
@FeatureToggle
public boolean hideSacksChange = false;
+ @Category(name = "Translator", desc = "Chat translator settings.")
@Expose
- @ConfigOption(
- name = "Translator",
- desc = "Click on a message to translate it to English.\n" +
- "Use §e/shcopytranslation§7 to translate from English.\n" +
- "§cTranslation is not guaranteed to be 100% accurate."
- )
- @ConfigEditorBoolean
- @FeatureToggle
- public boolean translator = false;
+ public TranslatorConfig translator = new TranslatorConfig();
@Expose
@ConfigOption(name = "SkyBlock XP in Chat", desc = "Send the SkyBlock XP messages into the chat.")
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java
index e5780d1494f9..b2fb03d76c80 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/FilterTypesConfig.java
@@ -105,6 +105,12 @@ public class FilterTypesConfig {
@FeatureToggle
public boolean sacrifice = false;
+ @Expose
+ @ConfigOption(name = "Garden Pest", desc = "Hide the message of no pests on garden.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean gardenNoPest = false;
+
@Expose
@ConfigOption(name = "Block Alpha Achievements", desc = "Hide achievement messages while on the Alpha network.")
@ConfigEditorBoolean
@@ -112,13 +118,13 @@ public class FilterTypesConfig {
public boolean hideAlphaAchievements = false;
@Expose
- @ConfigOption(name = "Hide Parkour", desc = "Hide parkour messages.")
+ @ConfigOption(name = "Hide Parkour", desc = "Hide parkour messages (starting, stopping, reaching a milestone).")
@ConfigEditorBoolean
@FeatureToggle
public boolean hideParkour = false;
@Expose
- @ConfigOption(name = "Hide Teleport pads", desc = "Hide annoying messages from teleport pads.")
+ @ConfigOption(name = "Hide Teleport Pads", desc = "Hide annoying messages when using teleport pads.")
@ConfigEditorBoolean
@FeatureToggle
public boolean hideTeleportPads = false;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/chat/TranslatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/chat/TranslatorConfig.java
new file mode 100644
index 000000000000..648afbd8739c
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/chat/TranslatorConfig.java
@@ -0,0 +1,37 @@
+package at.hannibal2.skyhanni.config.features.chat;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.features.chat.translation.TranslatableLanguage;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+import io.github.notenoughupdates.moulconfig.observer.Property;
+
+public class TranslatorConfig {
+
+ @Expose
+ @ConfigOption(
+ name = "Translate On Click",
+ desc = "Click on a message to translate it to English.\n" +
+ "Use §e/shcopytranslation§7 to translate from English.\n" +
+ "§cTranslation is not guaranteed to be 100% accurate."
+ )
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean translateOnClick = false;
+
+ @ConfigOption(name = "Language Name", desc = "The name of the language selected below. Note that languages marked as unknown might still be supported.")
+ @Expose
+ @ConfigEditorDropdown
+ public Property languageName = Property.of(TranslatableLanguage.ENGLISH);
+
+ @Expose
+ @ConfigOption(
+ name = "Language Code",
+ desc = "Enter a language code here to translate on chat click into another language. " +
+ "E.g. `es` for spanish or 'de' for german. Empty for english.")
+ @ConfigEditorText
+ public Property languageCode = Property.of("en");
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java
index b01647609efc..4beede62ba18 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/combat/CombatConfig.java
@@ -27,6 +27,7 @@ public class CombatConfig {
@Expose
@ConfigOption(name = "Armor Stack Display", desc = "")
@Accordion
+ // TODO rename to armor stack display
public StackDisplayConfig stackDisplayConfig = new StackDisplayConfig();
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/combat/MobsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/combat/MobsConfig.java
index 0d9c2f6944f5..fc46880fc538 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/combat/MobsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/combat/MobsConfig.java
@@ -3,6 +3,7 @@
import at.hannibal2.skyhanni.config.FeatureToggle;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
public class MobsConfig {
@@ -63,6 +64,11 @@ public class MobsConfig {
@FeatureToggle
public boolean lineToArachne = false;
+ @Expose
+ @ConfigOption(name = "Line to Arachne Width", desc = "The width of the line pointing to where Arachne is at.")
+ @ConfigEditorSlider(minStep = 1, minValue = 1, maxValue = 10)
+ public int lineToArachneWidth = 5;
+
@Expose
@ConfigOption(
name = "Area Boss Timer",
@@ -83,6 +89,15 @@ public class MobsConfig {
@FeatureToggle
public boolean showArachneSpawnTimer = true;
+ @Expose
+ @ConfigOption(
+ name = "Arachne Kill Timer", desc = "Shows how long it took to kill Arachne after the fight ends. " +
+ "§cDoes not show if you were not in the Sanctuary when it spawned."
+ )
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean arachneKillTimer = true;
+
@Expose
@ConfigOption(name = "Enderman TP Hider", desc = "Stops the Enderman Teleportation animation.")
@ConfigEditorBoolean
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/combat/damageindicator/DamageIndicatorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/combat/damageindicator/DamageIndicatorConfig.java
index 7e44df7f9e11..7010c7d35ff0 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/combat/damageindicator/DamageIndicatorConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/combat/damageindicator/DamageIndicatorConfig.java
@@ -171,7 +171,8 @@ public String toString() {
public boolean hideVanillaNametag = false;
@Expose
- @ConfigOption(name = "Time to Kill", desc = "Show the time it takes to kill the slayer boss.")
+ @ConfigOption(name = "Time to Kill", desc = "Show the time it takes to kill the slayer boss.\n" +
+ "§eRequires Damage Indicator to be active.")
@ConfigEditorBoolean
public boolean timeToKillSlayer = true;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/commands/CommandsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/commands/CommandsConfig.java
index 783d43d7486b..1747baee6348 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/commands/CommandsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/commands/CommandsConfig.java
@@ -18,6 +18,11 @@ public class CommandsConfig {
@Expose
public BetterWikiCommandConfig betterWiki = new BetterWikiCommandConfig();
+ @ConfigOption(name = "Reverse Party Transfer", desc = "")
+ @Accordion
+ @Expose
+ public ReversePartyTransferConfig reversePT = new ReversePartyTransferConfig();
+
@ConfigOption(name = "Party Commands", desc = "Shortens party commands and allows tab-completing for them. " +
"\n§eCommands: /pt, /pp, /pko, /pk, /pd §7(SkyBlock command §e/pt §7to check your play time will still work)")
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/commands/ReversePartyTransferConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/commands/ReversePartyTransferConfig.java
new file mode 100644
index 000000000000..555733667bd9
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/commands/ReversePartyTransferConfig.java
@@ -0,0 +1,27 @@
+package at.hannibal2.skyhanni.config.features.commands;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class ReversePartyTransferConfig {
+
+ @Expose
+ @ConfigOption(name = "Command", desc = "Adds §e/rpt §7to transfer a party back to its previous leader.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean command = true;
+
+ @Expose
+ @ConfigOption(name = "Clickable Message", desc = "Allows transfer message to be clicked to transfer a party back to its previous leader if it has been transferred to you.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean clickable = false;
+
+ @Expose
+ @ConfigOption(name = "Response Message", desc = "Sends a custom message to party chat when the party is reverse transferred.")
+ @ConfigEditorText
+ public String message = "Nuh Uh";
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java
index f2398e20740a..b0b55422404e 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/crimsonisle/CrimsonIsleConfig.java
@@ -26,6 +26,16 @@ public class CrimsonIsleConfig {
@Accordion
public MatriarchHelperConfig matriarchHelper = new MatriarchHelperConfig();
+ @Expose
+ @ConfigOption(name = "Miniboss Respawn Timer", desc = "Shows a timer for when minibosses will respawn.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean minibossRespawnTimer = false;
+
+ @Expose
+ @ConfigLink(owner = CrimsonIsleConfig.class, field = "minibossRespawnTimer")
+ public Position minibossTimerPosition = new Position(20, 50);
+
@Expose
@ConfigOption(name = "Pablo NPC Helper", desc = "Show a clickable message that grabs the flower needed from your sacks.")
@ConfigEditorBoolean
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java
index d6a0eb0645eb..56a17dfb921b 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugConfig.java
@@ -157,6 +157,11 @@ public class DebugConfig {
@ConfigEditorBoolean
public boolean neverFunnyTime = false;
+ @Expose
+ @ConfigOption(name = "Always Hoppity's", desc = "Always act as if Hoppity's Hunt is active.")
+ @ConfigEditorBoolean
+ public boolean alwaysHoppitys = false;
+
// Does not have a config element!
@Expose
public Position trackSoundPosition = new Position(0, 0);
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugMobConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugMobConfig.java
index c14c036980e2..1376c8b6cc52 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugMobConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DebugMobConfig.java
@@ -74,7 +74,7 @@ public static class MobDetection {
public HowToShow special = HowToShow.OFF;
@Expose
- @ConfigOption(name = "Show Invisible", desc = "Shows the mob even though they are invisible (do to invisibility effect) if looked at directly.")
+ @ConfigOption(name = "Show Invisible", desc = "Shows invisible mobs (due to invisibility effect) if looked at directly.")
@ConfigEditorBoolean
public boolean showInvisible = false;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java
index a42da18c1d4d..b62c88efc61f 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/DevConfig.java
@@ -1,5 +1,6 @@
package at.hannibal2.skyhanni.config.features.dev;
+import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.core.config.Position;
import at.hannibal2.skyhanni.config.features.dev.minecraftconsole.MinecraftConsoleConfig;
import com.google.gson.annotations.Expose;
@@ -72,6 +73,7 @@ public class DevConfig {
"§eThose are the folks that coded the mod for you for free :)"
)
@ConfigEditorBoolean
+ @FeatureToggle
public boolean fancyContributors = true;
@Expose
@@ -80,6 +82,7 @@ public class DevConfig {
desc = "Makes SkyHanni contributors' nametags fancy too. "
)
@ConfigEditorBoolean
+ @FeatureToggle
public boolean contributorNametags = true;
@Expose
@@ -87,6 +90,7 @@ public class DevConfig {
name = "Flip Contributors",
desc = "Make SkyHanni contributors appear upside down in the world.")
@ConfigEditorBoolean
+ @FeatureToggle
public boolean flipContributors = true;
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java
index 8dd606cd3f0c..9a2806f64e01 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dev/GraphConfig.java
@@ -21,6 +21,11 @@ public class GraphConfig {
@ConfigEditorKeybind(defaultKey = Keyboard.KEY_F)
public int placeKey = Keyboard.KEY_F;
+ @Expose
+ @ConfigOption(name = "Toggle Ghost Position", desc = "Creates or removes the Ghost Position. This helps editing nodes tht are in the air.")
+ @ConfigEditorKeybind(defaultKey = Keyboard.KEY_F)
+ public int toggleGhostPosition = Keyboard.KEY_NONE;
+
@Expose
@ConfigOption(name = "Select Key", desc = "Select the nearest node to be active. Double press to unselect.")
@ConfigEditorKeybind(defaultKey = -98) // Middle Mouse
@@ -37,7 +42,7 @@ public class GraphConfig {
public int exitKey = Keyboard.KEY_HOME;
@Expose
- @ConfigOption(name = "Edit Key", desc = "While holding the Key, edit the position of the active node with the minecraft movement controls.")
+ @ConfigOption(name = "Edit Key", desc = "While holding the Key, edit the position of the active node or the selection block with the minecraft movement controls.")
@ConfigEditorKeybind(defaultKey = Keyboard.KEY_TAB)
public int editKey = Keyboard.KEY_TAB;
@@ -76,6 +81,16 @@ public class GraphConfig {
@ConfigEditorKeybind(defaultKey = Keyboard.KEY_K)
public int tutorialKey = Keyboard.KEY_K;
+ @Expose
+ @ConfigOption(name = "Split Key", desc = "Key for splitting an edge that is between the active and the closed node.")
+ @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE)
+ public int splitKey = Keyboard.KEY_NONE;
+
+ @Expose
+ @ConfigOption(name = "Dissolve Key", desc = "Dissolve the active node into one edge if it only has two edges.")
+ @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE)
+ public int dissolveKey = Keyboard.KEY_NONE;
+
@Expose
@ConfigLink(owner = GraphConfig.class, field = "enabled")
public Position infoDisplay = new Position(20, 20);
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/DungeonConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/DungeonConfig.java
index b76039da1036..ec2bc0c4b0f9 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/DungeonConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/DungeonConfig.java
@@ -12,9 +12,13 @@ public class DungeonConfig {
@Expose
@ConfigOption(name = "Clicked Blocks", desc = "Highlight levers, chests, and Wither Essence when clicked in Dungeons.")
- @ConfigEditorBoolean
- @FeatureToggle
- public boolean highlightClickedBlocks = false;
+ @Accordion
+ public HighlightClickedBlocksConfig clickedBlocks = new HighlightClickedBlocksConfig();
+
+ @Expose
+ @ConfigOption(name = "Secret Chime", desc = "Play a sound effect when levers, chests, and wither essence are clicked in dungeons.")
+ @Accordion
+ public SecretChimeConfig secretChime = new SecretChimeConfig();
@Expose
@ConfigOption(name = "Milestones Display", desc = "Show the current milestone in Dungeons.")
@@ -67,6 +71,11 @@ public class DungeonConfig {
@FeatureToggle
public boolean architectNotifier = true;
+ @Expose
+ @ConfigOption(name = "Object Highlighter", desc = "Highlights various things in Dungeons.")
+ @Accordion
+ public ObjectHighlighterConfig objectHighlighter = new ObjectHighlighterConfig();
+
@Expose
@ConfigOption(name = "Object Hider", desc = "Hide various things in Dungeons.")
@Accordion
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/HighlightClickedBlocksConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/HighlightClickedBlocksConfig.java
new file mode 100644
index 000000000000..6ba7232b0dd7
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/HighlightClickedBlocksConfig.java
@@ -0,0 +1,61 @@
+package at.hannibal2.skyhanni.config.features.dungeon;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorButton;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class HighlightClickedBlocksConfig {
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Highlight levers, chests, and wither essence when clicked in Dungeons.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Chest Color", desc = "Color of clicked chests.")
+ @ConfigEditorColour
+ public String chestColor = "0:178:85:255:85";
+
+ @Expose
+ @ConfigOption(name = "Trapped Chest Color", desc = "Color of clicked trapped chests.")
+ @ConfigEditorColour
+ public String trappedChestColor = "0:178:0:170:0";
+
+ @Expose
+ @ConfigOption(name = "Locked Chest Color", desc = "Color of clicked locked chests.")
+ @ConfigEditorColour
+ public String lockedChestColor = "0:178:255:85:85";
+
+ @Expose
+ @ConfigOption(name = "Wither Essence Color", desc = "Color of clicked wither essence.")
+ @ConfigEditorColour
+ public String witherEssenceColor = "0:178:255:85:255";
+
+ @Expose
+ @ConfigOption(name = "Lever Color", desc = "Color of clicked levers.")
+ @ConfigEditorColour
+ public String leverColor = "0:178:255:255:85";
+
+ @Expose
+ @ConfigOption(name = "Show Text", desc = "Shows a text saying what you clicked with the highlight.")
+ @ConfigEditorBoolean
+ public boolean showText = true;
+
+ @Expose
+ @ConfigOption(name = "Random Color", desc = "If enabled makes the colors random.")
+ @ConfigEditorBoolean
+ public boolean randomColor = false;
+
+ @ConfigOption(name = "Reset Colors", desc = "Resets the colors of the highlights to default ones.")
+ @ConfigEditorButton(buttonText = "Reset")
+ public Runnable reset = () -> {
+ chestColor = "0:178:85:255:85";
+ trappedChestColor = "0:178:0:170:0";
+ lockedChestColor = "0:178:255:85:85";
+ witherEssenceColor = "0:178:255:85:255";
+ leverColor = "0:178:255:255:85";
+ };
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/ObjectHighlighterConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/ObjectHighlighterConfig.java
new file mode 100644
index 000000000000..385c162c6623
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/ObjectHighlighterConfig.java
@@ -0,0 +1,72 @@
+package at.hannibal2.skyhanni.config.features.dungeon;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.Accordion;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+import io.github.notenoughupdates.moulconfig.observer.Property;
+
+public class ObjectHighlighterConfig {
+
+ // TODO move some stuff from DungeonConfig into this
+
+ @Expose
+ @ConfigOption(name = "Stared", desc = "")
+ @Accordion
+ public StarredConfig starred = new StarredConfig();
+
+ public static class StarredConfig {
+ @Expose
+ @ConfigOption(name = "Highlight Starred", desc = "Highlights all starred mobs in one colour.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public Property highlight = Property.of(true);
+
+ /*
+ TODO for someone who has time
+ @Expose
+ @ConfigOption(name = "Show Outline", desc = "Shows only a outline instead of a full highlight.")
+ @ConfigEditorBoolean
+ public Property showOutline = Property.of(true); */
+
+ @ConfigOption(
+ name = "No Chroma",
+ desc = "§cThe chroma setting for the color is currently not working!"
+ )
+ @ConfigEditorInfoText
+ public String info;
+
+ @Expose
+ @ConfigOption(name = "Colour", desc = "Color in which the stared mobs are highlighted.")
+ @ConfigEditorColour
+ public Property colour = Property.of("0:60:255:255:0");
+ }
+
+ @Expose
+ @ConfigOption(name = "Fels Skull", desc = "")
+ @Accordion
+ public FelConfig fel = new FelConfig();
+
+ public static class FelConfig {
+
+ @Expose
+ @ConfigOption(name = "Highlight Fels Skull", desc = "Highlights fels that are not yet active.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public Property highlight = Property.of(true);
+
+ @Expose
+ @ConfigOption(name = "Draw Line", desc = "Draws a line to fels skulls. Works only if the highlight is enabled.")
+ @ConfigEditorBoolean
+ public Boolean line = false;
+
+ @Expose
+ @ConfigOption(name = "Colour", desc = "Color for the fel skull and line.")
+ @ConfigEditorColour
+ public Property colour = Property.of("0:200:255:0:255");
+ }
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/SecretChimeConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/SecretChimeConfig.java
new file mode 100644
index 000000000000..99a1e103d212
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/dungeon/SecretChimeConfig.java
@@ -0,0 +1,40 @@
+package at.hannibal2.skyhanni.config.features.dungeon;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.features.dungeon.DungeonSecretChime;
+import at.hannibal2.skyhanni.utils.OSUtils;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorButton;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class SecretChimeConfig {
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Play a sound effect when levers, chests, and wither essence are clicked in dungeons.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Secret Chime Sound", desc = "The sound played for the secret chime.")
+ @ConfigEditorText
+ public String soundName = "random.orb";
+
+ @Expose
+ @ConfigOption(name = "Pitch", desc = "The pitch of the secret chime sound.")
+ @ConfigEditorSlider(minValue = 0.5f, maxValue = 2.0f, minStep = 0.1f)
+ public float soundPitch = 1.0f;
+
+ @ConfigOption(name = "Sounds",
+ desc = "Click to open the list of available sounds.\n" +
+ "§l§cWarning: Clicking this will open a webpage in your browser."
+ )
+ @ConfigEditorButton(buttonText = "OPEN")
+ public Runnable soundsListURL = () -> OSUtils.openBrowser("https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2213619-1-8-all-playsound-sound-arguments");
+
+ @ConfigOption(name = "Play Sound", desc = "Plays current secret chime sound.")
+ @ConfigEditorButton(buttonText = "Play")
+ public Runnable checkSound = DungeonSecretChime::playSound;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java
index 251ce51ba807..57d3e0699a30 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/EventConfig.java
@@ -2,6 +2,7 @@
import at.hannibal2.skyhanni.config.features.event.bingo.BingoConfig;
import at.hannibal2.skyhanni.config.features.event.diana.DianaConfig;
+import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEggsConfig;
import at.hannibal2.skyhanni.config.features.event.waypoints.LobbyWaypointsConfig;
import at.hannibal2.skyhanni.config.features.event.winter.WinterConfig;
import com.google.gson.annotations.Expose;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/bingo/BingoCardConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/bingo/BingoCardConfig.java
index b317f979a9de..cd7a4155842b 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/bingo/BingoCardConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/bingo/BingoCardConfig.java
@@ -16,7 +16,7 @@ public class BingoCardConfig {
public boolean enabled = true;
@Expose
- @ConfigOption(name = "Quick Toggle", desc = "Quickly toggle the Bingo Card or the step helper by sneaking with SkyBlock Menu in hand.")
+ @ConfigOption(name = "Quick Toggle", desc = "Quickly show/hide the Bingo Card (when enabled above) or the step helper by sneaking with SkyBlock Menu in hand.")
@ConfigEditorBoolean
public boolean quickToggle = true;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/DianaConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/DianaConfig.java
index 132c0d827127..3e2c6724dd64 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/DianaConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/DianaConfig.java
@@ -36,7 +36,8 @@ public class DianaConfig {
public boolean burrowsNearbyDetection = false;
@Expose
- @ConfigOption(name = "Line To Next", desc = "Show a line to the closest burrow or guess location.")
+ @ConfigOption(name = "Line To Next", desc = "Show a line to the closest burrow or guess location.\n" +
+ "§eRequires Burrow particle detection.")
@ConfigEditorBoolean
public boolean lineToNext = true;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/IgnoredWarpsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/IgnoredWarpsConfig.java
index 8b6edcb9aa66..f33820772afa 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/IgnoredWarpsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/diana/IgnoredWarpsConfig.java
@@ -16,4 +16,9 @@ public class IgnoredWarpsConfig {
@ConfigEditorBoolean
public boolean wizard = false;
+ @Expose
+ @ConfigOption(name = "Stonks", desc = "Ignore the Stonks warp point (because it is new).")
+ @ConfigEditorBoolean
+ public boolean stonks = false;
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityCallWarningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityCallWarningConfig.java
new file mode 100644
index 000000000000..6e4ccf027305
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityCallWarningConfig.java
@@ -0,0 +1,47 @@
+package at.hannibal2.skyhanni.config.features.event.hoppity;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.utils.OSUtils;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorButton;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+import io.github.notenoughupdates.moulconfig.observer.Property;
+import org.lwjgl.input.Keyboard;
+
+public class HoppityCallWarningConfig {
+
+ @Expose
+ @ConfigOption(name = "Hoppity Call Warning", desc = "Warn when hoppity is calling you.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Accept Call Hotkey",
+ desc = "Accept the call from hoppity by pressing this keybind."
+ )
+ @ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE)
+ public int acceptHotkey = Keyboard.KEY_NONE;
+
+ @Expose
+ @ConfigOption(name = "Warning Sound", desc = "The sound that plays when hoppity calls.\n" +
+ "§eYou can use custom sounds, put it in the §bskyhanni/sounds §efolder in your resource pack.\n" +
+ "§eThen write §bskyhanni:yourfilename\n" +
+ "§cMust be a .ogg file")
+ @ConfigEditorText
+ public Property hoppityCallSound = Property.of("note.pling");
+
+ @Expose
+ @ConfigOption(name = "Flash Color", desc = "Color of the screen when flashing")
+ @ConfigEditorColour
+ public String flashColor = "0:127:0:238:255";
+
+ @ConfigOption(name = "Sounds", desc = "Click to open the list of available sounds.")
+ @ConfigEditorButton(buttonText = "OPEN")
+ public Runnable sounds = () -> OSUtils.openBrowser("https://www.minecraftforum.net/forums/mapping-and-modding-java-edition/mapping-and-modding-tutorials/2213619-1-8-all-playsound-sound-arguments");
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java
similarity index 84%
rename from src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java
rename to src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java
index 5536d56914fe..c270dae8bbe7 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/HoppityEggsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEggsConfig.java
@@ -1,10 +1,12 @@
-package at.hannibal2.skyhanni.config.features.event;
+package at.hannibal2.skyhanni.config.features.event.hoppity;
import at.hannibal2.skyhanni.config.FeatureToggle;
import at.hannibal2.skyhanni.config.core.config.Position;
import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.Accordion;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorText;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
@@ -12,6 +14,16 @@
public class HoppityEggsConfig {
+ @Expose
+ @ConfigOption(name = "Hoppity Abiphone Calls", desc = "")
+ @Accordion
+ public HoppityCallWarningConfig hoppityCallWarning = new HoppityCallWarningConfig();
+
+ @Expose
+ @ConfigOption(name = "Event Summary", desc = "")
+ @Accordion
+ public HoppityEventSummaryConfig eventSummary = new HoppityEventSummaryConfig();
+
@Expose
@ConfigOption(name = "Hoppity Waypoints", desc = "Toggle guess waypoints for Hoppity's Hunt.")
@ConfigEditorBoolean
@@ -155,6 +167,30 @@ public class HoppityEggsConfig {
@FeatureToggle
public boolean compactChat = false;
+ @Expose
+ @ConfigOption(name = "Compacted Rarity", desc = "Show rarity of found rabbit in Compacted chat messages.")
+ @ConfigEditorDropdown
+ public CompactRarityTypes rarityInCompact = CompactRarityTypes.NEW;
+
+ public enum CompactRarityTypes {
+ NONE("Neither"),
+ NEW("New Rabbits"),
+ DUPE("Duplicate Rabbits"),
+ BOTH("New & Duplicate Rabbits"),
+ ;
+
+ private final String name;
+
+ CompactRarityTypes(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
@Expose
@ConfigOption(
name = "Rabbit Pet Warning",
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEventSummaryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEventSummaryConfig.java
new file mode 100644
index 000000000000..618b15f7fc41
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/hoppity/HoppityEventSummaryConfig.java
@@ -0,0 +1,73 @@
+package at.hannibal2.skyhanni.config.features.event.hoppity;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class HoppityEventSummaryConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Show a summary of your Hoppity Hunt stats when the event is over.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(
+ name = "Viewing Stats",
+ desc = "View current and past event stats at any time using §b/shhoppitystats§7."
+ )
+ @ConfigEditorInfoText
+ public String commandInfo;
+
+ @Expose
+ @ConfigOption(
+ name = "Stats List",
+ desc = "Drag text to change what displays in the summary card."
+ )
+ @ConfigEditorDraggableList
+ public List statDisplayList = new ArrayList<>(Arrays.asList(
+ HoppityStat.MEAL_EGGS_FOUND,
+ HoppityStat.HOPPITY_RABBITS_BOUGHT,
+ HoppityStat.SIDE_DISH_EGGS,
+ HoppityStat.MILESTONE_RABBITS,
+ HoppityStat.EMPTY_1,
+ HoppityStat.NEW_RABBITS,
+ HoppityStat.EMPTY_2,
+ HoppityStat.DUPLICATE_RABBITS
+ ));
+
+ public enum HoppityStat {
+ MEAL_EGGS_FOUND("§7You found §b45§7/§a47 §6Chocolate Meal Eggs§7."),
+ HOPPITY_RABBITS_BOUGHT("§7You bought §b7 §fRabbits §7from §aHoppity§7."),
+ SIDE_DISH_EGGS("§7You found §b4 §6§lSide Dish §r§6Eggs §7in the §6Chocolate Factory§7."),
+ MILESTONE_RABBITS("§7You claimed §b2 §6§lMilestone Rabbits§7."),
+ EMPTY_1(""),
+ NEW_RABBITS("§7Unique Rabbits: §b7\n §f1 §7- §a1 §7- §91 §7- §51 §7- §61 §7- §d1 §7- §b1"),
+ EMPTY_2(""),
+ DUPLICATE_RABBITS("§7Duplicate Rabbits: §c10\n §f4 §7- §a3 §7- §92 §7- §51 §7- §60 §7- §d0 §7- §b0\n §6+250,000,000 Chocolate"),
+ EMPTY_3(""),
+ STRAY_RABBITS("§7Stray Rabbits: §f20\n §f10 §7- §a6 §7- §93 §7- §51 §7- §60 §7- §d0 §7- §b0\n §6+8,000,000 Chocolate\n §c* §c§oRequires Stray Tracker being enabled to work."),
+ EMPTY_4(""),
+ TIME_IN_CF("§7You spent §b4h 36m §7in the §6Chocolate Factory§7."),
+ ;
+
+ private final String display;
+
+ HoppityStat(String display) {
+ this.display = display;
+ }
+
+ @Override
+ public String toString() {
+ return display;
+ }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/ChristmasPresentConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/ChristmasPresentConfig.java
index 1ecb9e089fc9..cb3ac01fb785 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/ChristmasPresentConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/ChristmasPresentConfig.java
@@ -8,18 +8,20 @@
public class ChristmasPresentConfig {
@Expose
- @ConfigOption(name = "Present Waypoints", desc = "Show all Present waypoints")
+ @ConfigOption(name = "Present Waypoints", desc = "Show all Present waypoints.")
@ConfigEditorBoolean
@FeatureToggle
public boolean allWaypoints = false;
+ // TODO confirm if this toggle actually does anything, ar there helper waypoints at all?
@Expose
- @ConfigOption(name = "Entrance Waypoints", desc = "Show helper waypoints to .")
+ @ConfigOption(name = "Entrance Waypoints", desc = "Show helper waypoints.")
@ConfigEditorBoolean
public boolean allEntranceWaypoints = false;
+
@Expose
- @ConfigOption(name = "Only Closest", desc = "Only show the closest waypoint")
+ @ConfigOption(name = "Only Closest", desc = "Only show the closest waypoint.")
@ConfigEditorBoolean
public boolean onlyClosest = false;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/HalloweenBasketConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/HalloweenBasketConfig.java
index 153e55c8bacc..f0b31f6490b9 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/HalloweenBasketConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/event/waypoints/HalloweenBasketConfig.java
@@ -21,7 +21,7 @@ public class HalloweenBasketConfig {
public boolean allEntranceWaypoints = false;
@Expose
- @ConfigOption(name = "Only Closest", desc = "Only show the closest waypoint")
+ @ConfigOption(name = "Only Closest", desc = "Only show the closest waypoint.")
@ConfigEditorBoolean
public boolean onlyClosest = true;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/TotemOfCorruptionConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/TotemOfCorruptionConfig.java
index c1454b8f1897..783f6474c224 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/fishing/TotemOfCorruptionConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/fishing/TotemOfCorruptionConfig.java
@@ -29,7 +29,8 @@ public class TotemOfCorruptionConfig {
public int distanceThreshold = 16;
@Expose
- @ConfigOption(name = "Hide Particles", desc = "Hide the particles of the Totem of Corruption.")
+ @ConfigOption(name = "Hide Particles", desc = "Hide the particles of the Totem of Corruption.\n" +
+ "§eRequires the Overlay to be active.")
@ConfigEditorBoolean
public boolean hideParticles = true;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/composter/ComposterConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/composter/ComposterConfig.java
index 9712573fb728..c91ffbbca371 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/composter/ComposterConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/composter/ComposterConfig.java
@@ -91,6 +91,7 @@ public String toString() {
desc = "Warn when the Composter gets close to empty, even outside Garden."
)
@ConfigEditorBoolean
+ // TODO rename to warnAlmostEmpty
public boolean warnAlmostClose = false;
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/CustomSpeedConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/CustomSpeedConfig.java
index 16416372ae5e..ffe493fc926c 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/CustomSpeedConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/CustomSpeedConfig.java
@@ -65,11 +65,9 @@ public class CustomSpeedConfig {
@Expose
@ConfigOption(name = "Cactus", desc = "Suggested farm speed:\n" +
- "§eNormal§7: §f✦ 400 speed\n" +
- "§eRacing Helmet§7: §f✦ 464 speed\n" +
- "§eBlack Cat§7: §f✦ 464 speed")
+ "§eYaw 90§7: §f✦ 464 speed")
@ConfigEditorSlider(minValue = 1, maxValue = 500, minStep = 1)
- public Property cactus = Property.of(400f);
+ public Property cactus = Property.of(464f);
// TODO does other speed settings exist?
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/OptimalSpeedConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/OptimalSpeedConfig.java
index 6e44ecfec217..e7a11de1f957 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/OptimalSpeedConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/optimalspeed/OptimalSpeedConfig.java
@@ -17,7 +17,7 @@ public class OptimalSpeedConfig {
public boolean showOnHUD = false;
@Expose
- @ConfigOption(name = "Warning Title", desc = "Warn via title when you don't have the optimal speed.")
+ @ConfigOption(name = "Wrong Speed Warning", desc = "Warn via title and chat message when you don't have the optimal speed.")
@ConfigEditorBoolean
public boolean warning = false;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestFinderConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestFinderConfig.java
index 20f840084e54..587bfa73fd27 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestFinderConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestFinderConfig.java
@@ -78,6 +78,11 @@ public String toString() {
@ConfigLink(owner = PestFinderConfig.class, field = "showDisplay")
public Position position = new Position(-350, 200, 1.3f);
+ @Expose
+ @ConfigOption(name = "No Pests Title", desc = "Show a Title in case of No pests. Useful if you are using the §eGarden Pest Chat Filter")
+ @ConfigEditorBoolean
+ public boolean noPestTitle = false;
+
@Expose
@ConfigOption(name = "Teleport Hotkey", desc = "Press this key to warp to the nearest plot with pests on it.")
@ConfigEditorKeybind(defaultKey = Keyboard.KEY_NONE)
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java
index 0462554689f3..58ba1892273a 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/pests/PestWaypointConfig.java
@@ -33,6 +33,14 @@ public class PestWaypointConfig {
@ConfigEditorBoolean
public boolean drawLine = true;
+ @Expose
+ @ConfigOption(
+ name = "Show Middle",
+ desc = "Also show a waypoint to the middle of a plot. This can help determine if the tracker is pointing to the middle instead of a pest."
+ )
+ @ConfigEditorBoolean
+ public boolean showMiddle = false;
+
@Expose
@ConfigOption(
name = "Show For Seconds",
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/DropsStatisticsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/DropsStatisticsConfig.java
index 078f82c5d99d..aecedf0b07d3 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/DropsStatisticsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/garden/visitor/DropsStatisticsConfig.java
@@ -16,6 +16,7 @@
import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.ACCEPTED;
import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.COINS_SPENT;
import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.COPPER;
+import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.COPPER_DYE;
import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.DEDICATION_IV;
import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.DENIED;
import static at.hannibal2.skyhanni.config.features.garden.visitor.DropsStatisticsConfig.DropsStatisticsTextEntry.FARMING_EXP;
@@ -55,7 +56,8 @@ public class DropsStatisticsConfig {
COINS_SPENT,
OVERGROWN_GRASS,
GREEN_BANDANA,
- DEDICATION_IV
+ DEDICATION_IV,
+ COPPER_DYE
));
/**
@@ -89,6 +91,7 @@ public enum DropsStatisticsTextEntry implements HasLegacyId {
CULTIVATING_I("§b1 §9Cultivating I", 15),
REPLENISH_I("§b1 §9Replenish I", 16),
DELICATE("§b1 §9Delicate V"),
+ COPPER_DYE("§b1 §8Copper Dye"),
;
private final String str;
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java
index 1c3f92b4748c..74ab755c9940 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/GUIConfig.java
@@ -66,6 +66,11 @@ public class GUIConfig {
@Accordion
public HotbarConfig hotbar = new HotbarConfig();
+ @Expose
+ @ConfigOption(name = "Xp Bar", desc = "Settings for adjusting the xp bar")
+ @Accordion
+ public XPBarConfig xpBar = new XPBarConfig();
+
@Expose
@ConfigOption(name = "Marked Players", desc = "Players that got marked with §e/shmarkplayer§7.")
@Accordion
@@ -81,6 +86,11 @@ public class GUIConfig {
@Accordion
public TextBoxConfig customTextBox = new TextBoxConfig();
+ @Expose
+ @ConfigOption(name = "Tab Widget", desc = "")
+ @Accordion
+ public TabWidgetConfig tabWidget = new TabWidgetConfig();
+
@Expose
@ConfigOption(name = "In-Game Date", desc = "")
@Accordion
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/TabWidgetConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/TabWidgetConfig.java
new file mode 100644
index 000000000000..2d4b27cb1459
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/TabWidgetConfig.java
@@ -0,0 +1,40 @@
+package at.hannibal2.skyhanni.config.features.gui;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.PositionList;
+import at.hannibal2.skyhanni.features.gui.TabWidgetDisplay;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TabWidgetConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Enables the gui elements for the selected widgets.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @ConfigOption(name = "Not working Info", desc = "If the information isn't shown in the tablist it won't show anything. Use /widget to turn on the information you need.")
+ @ConfigEditorInfoText
+ public String text1;
+
+ @ConfigOption(name = "Enable Info", desc = "Drag only one new value at time into the list, since the default locations are all the same.")
+ @ConfigEditorInfoText
+ public String text2;
+
+ @Expose
+ @ConfigOption(name = "Widgets", desc = "")
+ @ConfigEditorDraggableList
+ public List display = new ArrayList<>();
+
+ @Expose
+ @ConfigLink(owner = TabWidgetConfig.class, field = "enabled")
+ public PositionList displayPositions = new PositionList(TabWidgetDisplay.getEntries().size());
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/XPBarConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/XPBarConfig.java
new file mode 100644
index 000000000000..bd2f2cac90ad
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/XPBarConfig.java
@@ -0,0 +1,31 @@
+package at.hannibal2.skyhanni.config.features.gui;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import at.hannibal2.skyhanni.config.core.config.Position;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class XPBarConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Allows for moving and scaling the Xp bar in the SkyHanni GUI Editor.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @ConfigOption(name = "§cNotice", desc = "This option will be §c§lincompatible §r§7with mods that change the xp bar. Eg: §eApec§7.")
+ @ConfigEditorInfoText
+ public String notice = "";
+
+ @Expose
+ @ConfigLink(owner = XPBarConfig.class, field = "enabled")
+ public Position position = new Position(20, 20);
+
+ @Expose
+ @ConfigOption(name = "Show Outside Skyblock", desc = "Shows the XP bar outside of SkyBlock.")
+ @ConfigEditorBoolean
+ public boolean showOutsideSkyblock = false;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/AlignmentConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/AlignmentConfig.java
index 0b62bfcbcc2b..fec0847f4415 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/AlignmentConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/AlignmentConfig.java
@@ -4,6 +4,7 @@
import at.hannibal2.skyhanni.utils.RenderUtils.VerticalAlignment;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
public class AlignmentConfig {
@@ -17,4 +18,9 @@ public class AlignmentConfig {
@ConfigOption(name = "Vertical Alignment", desc = "Alignment for the scoreboard on the vertical axis.")
@ConfigEditorDropdown
public VerticalAlignment verticalAlignment = VerticalAlignment.CENTER;
+
+ @Expose
+ @ConfigOption(name = "Margin", desc = "Space between the border of your screen and the scoreboard.")
+ @ConfigEditorSlider(minValue = 0, maxValue = 50, minStep = 1)
+ public int margin = 0;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/BackgroundConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/BackgroundConfig.java
index 18ffd3c5853c..be0f5f148678 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/BackgroundConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/BackgroundConfig.java
@@ -56,12 +56,20 @@ public class BackgroundConfig {
@Expose
@ConfigOption(
- name = "Use Custom Background Image",
+ name = "Custom Background Image",
desc = "Put that image into a resource pack, using the path \"skyhanni/scoreboard.png\"."
)
@ConfigEditorBoolean
public boolean useCustomBackgroundImage = false;
+ @Expose
+ @ConfigOption(
+ name = "Background Image Opacity",
+ desc = "The opacity of the custom background image."
+ )
+ @ConfigEditorSlider(minValue = 0, maxValue = 100, minStep = 1)
+ public int customBackgroundImageOpacity = 100;
+
@Expose
@ConfigOption(
name = "Custom Background",
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/DisplayConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/DisplayConfig.java
index c9e330b33952..2f0eb502e509 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/DisplayConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/DisplayConfig.java
@@ -65,6 +65,16 @@ public class DisplayConfig {
@ConfigEditorBoolean
public boolean displayNumbersFirst = false;
+ @Expose
+ @ConfigOption(name = "Hide coins earned/lost", desc = "Hide the amount of coins earned or lost.")
+ @ConfigEditorBoolean
+ public boolean hideCoinsDifference = false;
+
+ @Expose
+ @ConfigOption(name = "Use Custom Lines", desc = "Use custom lines instead of the default ones.")
+ @ConfigEditorBoolean
+ public boolean useCustomLines = true;
+
@Expose
@ConfigOption(name = "Show unclaimed bits", desc = "Show the amount of available Bits that can still be claimed.")
@ConfigEditorBoolean
@@ -119,6 +129,16 @@ public String toString() {
}
}
+ @Expose
+ @ConfigOption(name = "SkyBlock Time 24h Format", desc = "Display the current SkyBlock time in 24hr format rather than 12h Format.")
+ @ConfigEditorBoolean
+ public boolean skyblockTime24hFormat = false;
+
+ @Expose
+ @ConfigOption(name = "SkyBlock Time Exact Minutes", desc = "Display the exact minutes in the SkyBlock time, rather than only 10 minute increments.")
+ @ConfigEditorBoolean
+ public boolean skyblockTimeExactMinutes = true;
+
@Expose
@ConfigOption(name = "Line Spacing", desc = "The amount of space between each line.")
@ConfigEditorSlider(minValue = 0, maxValue = 20, minStep = 1)
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/TitleAndFooterConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/TitleAndFooterConfig.java
index 6bb517d6ffb3..1c3bb4304509 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/TitleAndFooterConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/gui/customscoreboard/TitleAndFooterConfig.java
@@ -23,10 +23,14 @@ public class TitleAndFooterConfig {
public Property customTitle = Property.of("&6&lSKYBLOCK");
@Expose
- @ConfigOption(name = "Hypixel's Title Animation", desc = "Will overwrite the custom title with Hypixel's title animation." +
- "\nWill also include \"COOP\" if you are in a coop.")
+ @ConfigOption(name = "Use Custom Title", desc = "Use a custom title instead of the default Hypixel title.")
@ConfigEditorBoolean
- public boolean useHypixelTitleAnimation = false;
+ public boolean useCustomTitle = true;
+
+ @Expose
+ @ConfigOption(name = "Use Custom Title Outside SkyBlock", desc = "Use a custom title outside of SkyBlock.")
+ @ConfigEditorBoolean
+ public boolean useCustomTitleOutsideSkyBlock = false;
@Expose
@ConfigOption(name = "Custom Footer", desc = "What should be displayed as the footer of the scoreboard." +
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/GetFromSackConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/GetFromSackConfig.java
index 03990536fd1f..ee77d6664317 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/GetFromSackConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/GetFromSackConfig.java
@@ -3,6 +3,7 @@
import at.hannibal2.skyhanni.config.FeatureToggle;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
public class GetFromSackConfig {
@@ -23,4 +24,14 @@ public class GetFromSackConfig {
@ConfigEditorBoolean
@FeatureToggle
public boolean superCraftGFS = true;
+
+ @Expose
+ @ConfigOption(name = "Default GfS", desc = "If you don't provide an amount, a default one will be used instead. Queued GfS needs to be on in order for this to work.")
+ @ConfigEditorBoolean
+ public boolean defaultGFS = false;
+
+ @Expose
+ @ConfigOption(name = "Default Amount GfS", desc = "The default amount of items used when an amount isn't provided.")
+ @ConfigEditorSlider(minValue = 1, maxValue = 64, minStep = 1)
+ public int defaultAmountGFS = 1;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java
index 0c7556c30719..cf162ad7b6a8 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/InventoryConfig.java
@@ -73,6 +73,11 @@ public class InventoryConfig {
@Accordion
public HideNotClickableConfig hideNotClickable = new HideNotClickableConfig();
+ @Expose
+ @ConfigOption(name = "Personal Compactor Overlay", desc = "Overlay for the Personal Compactor and Deletor.")
+ @Accordion
+ public PersonalCompactorConfig personalCompactor = new PersonalCompactorConfig();
+
@Expose
@ConfigOption(name = "RNG Meter", desc = "")
@Accordion
@@ -118,6 +123,11 @@ public class InventoryConfig {
@Accordion
public PageScrollingConfig pageScrolling = new PageScrollingConfig();
+ @Expose
+ @ConfigOption(name = "Magical Power Display", desc = "")
+ @Accordion
+ public MagicalPowerConfig magicalPower = new MagicalPowerConfig();
+
@Expose
@ConfigOption(name = "Item Number", desc = "Showing the item number as a stack size for these items.")
@ConfigEditorDraggableList
@@ -146,9 +156,10 @@ public enum ItemNumberEntry implements HasLegacyId {
BOTTLE_OF_JYRRE("§bBottle Of Jyrre", 15),
DARK_CACAO_TRUFFLE("§bDark Cacao Truffle"),
EDITION_NUMBER("§bEdition Number", 16),
+ ENCHANTING_EXP("§bEnchanting EXP (Superpairs)"),
BINGO_GOAL_RANK("§bBingo Goal Rank"),
SKYBLOCK_LEVEL("§bSkyblock Level"),
- BESTIARY_LEVEL("§bBestiary Level")
+ BESTIARY_LEVEL("§bBestiary Level"),
;
private final String str;
@@ -263,4 +274,16 @@ public String toString() {
@ConfigEditorBoolean
@FeatureToggle
public boolean stonkOfStonkPrice = true;
+
+ @Expose
+ @ConfigOption(name = "Minister in Calendar", desc = "Show the Minister with their perk in the Calendar.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean ministerInCalendar = true;
+
+ @Expose
+ @ConfigOption(name = "Show hex as actual color", desc = "Changes the color of hex codes to the actual color.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean hexAsColorInLore = true;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/MagicalPowerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/MagicalPowerConfig.java
new file mode 100644
index 000000000000..ef6b76da336a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/MagicalPowerConfig.java
@@ -0,0 +1,19 @@
+package at.hannibal2.skyhanni.config.features.inventory;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class MagicalPowerConfig {
+ @Expose
+ @ConfigOption(name = "Magical Power Display", desc = "Show Magical Power as stack size inside Accessory Bag and Auction House.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Colored", desc = "Whether to make the numbers colored.")
+ @ConfigEditorBoolean
+ public boolean colored = false;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/PersonalCompactorConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/PersonalCompactorConfig.java
new file mode 100644
index 000000000000..3ded2e8124a1
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/PersonalCompactorConfig.java
@@ -0,0 +1,51 @@
+package at.hannibal2.skyhanni.config.features.inventory;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDropdown;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorKeybind;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+import org.lwjgl.input.Keyboard;
+
+public class PersonalCompactorConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Enable showing what items are inside your personal compactor/deletor.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Visibility Mode", desc = "Choose when to show the overlay.")
+ @ConfigEditorDropdown
+ public VisibilityMode visibilityMode = VisibilityMode.EXCEPT_KEYBIND;
+
+ public enum VisibilityMode {
+ ALWAYS("Always"),
+ KEYBIND("Keybind Held"),
+ EXCEPT_KEYBIND("Except Keybind Held"),
+ ;
+
+ private final String name;
+
+ VisibilityMode(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ @Expose
+ @ConfigOption(name = "Keybind", desc = "The keybind to hold to show the overlay.")
+ @ConfigEditorKeybind(defaultKey = Keyboard.KEY_LSHIFT)
+ public int keybind = Keyboard.KEY_LSHIFT;
+
+ @Expose
+ @ConfigOption(name = "Show On/Off", desc = "Show whether the Personal Compactor/Deletor is currently turned on or off.")
+ @ConfigEditorBoolean
+ public boolean showToggle = true;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java
index f6818d2c8969..5f54e707d010 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/inventory/helper/HelperConfig.java
@@ -74,6 +74,12 @@ public static class EnchantingConfig {
@ConfigEditorBoolean
@FeatureToggle
public boolean ultraRareBookAlert = false;
+
+ @Expose
+ @ConfigOption(name = "Guardian Reminder", desc = "Sends a warning when opening the Experimentation Table without a §9§lGuardian Pet §7equipped.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean guardianReminder = false;
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java
index 08c8ec894a71..5f242ecd5f47 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/mining/MiningConfig.java
@@ -96,4 +96,10 @@ public class MiningConfig {
@ConfigEditorBoolean
@FeatureToggle
public boolean highlightYourGoldenGoblin = true;
+
+ @Expose
+ @ConfigOption(name = "Forge GfS", desc = "Get Forge ingredients of a recipe.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean forgeGfs = false;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/BitsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/BitsConfig.java
index 9b3cc32e821d..7179a2aabdcc 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/misc/BitsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/BitsConfig.java
@@ -7,6 +7,25 @@
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
public class BitsConfig {
+
+ @Expose
+ @ConfigOption(name = "Bulk Buy Cookie Time", desc = "Corrects the time for cookies if bought in bulk on the buy item.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean bulkBuyCookieTime = true;
+
+ @Expose
+ @ConfigOption(name = "Bits on Cookie", desc = "Show the bits you would gain on a cookies.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean showBitsOnCookie = true;
+
+ @Expose
+ @ConfigOption(name = "Bits on Cookie Change", desc = "Show the change in available bits on cookies.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean showBitsChangeOnCookie = false;
+
@Expose
@ConfigOption(name = "Enable No Bits Warning", desc = "Alerts you when you have no bits available.")
@ConfigEditorBoolean
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/LastServersConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/LastServersConfig.java
new file mode 100644
index 000000000000..a488b52e9071
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/LastServersConfig.java
@@ -0,0 +1,21 @@
+package at.hannibal2.skyhanni.config.features.misc;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class LastServersConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Receive notifications when you rejoin a server you have previously joined.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = false;
+
+ @Expose
+ @ConfigOption(name = "Notification Time", desc = "Get notified if you rejoin a server within the specified number of seconds.")
+ @ConfigEditorSlider(minValue = 5, maxValue = 300, minStep = 1)
+ public int warnTime = 60;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java
index 6843d094877e..1464d35ada2d 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/MiscConfig.java
@@ -15,6 +15,7 @@
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorDraggableList;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+import io.github.notenoughupdates.moulconfig.observer.Property;
import java.util.ArrayList;
import java.util.List;
@@ -48,8 +49,9 @@ public class MiscConfig {
public HideArmorConfig hideArmor2 = new HideArmorConfig();
@Expose
- @ConfigOption(name = "Potion Effects", desc = "")
+ @ConfigOption(name = "Non-God Pot Effects", desc = "")
@Accordion
+ // TODO rename nonGodPotEffect
public PotionEffectsConfig potionEffect = new PotionEffectsConfig();
@Expose
@@ -107,10 +109,20 @@ public class MiscConfig {
@Accordion
public PatcherCoordsWaypointConfig patcherCoordsWaypoint = new PatcherCoordsWaypointConfig();
+ @Expose
+ @ConfigOption(name = "Reminders", desc = "")
+ @Accordion
+ public RemindersConfig reminders = new RemindersConfig();
+
+ @Expose
+ @ConfigOption(name = "Last Servers", desc = "")
+ @Accordion
+ public LastServersConfig lastServers = new LastServersConfig();
+
@Expose
@ConfigOption(name = "Show Outside SkyBlock", desc = "Show these features outside of SkyBlock.")
@ConfigEditorDraggableList
- public List showOutsideSB = new ArrayList<>();
+ public Property> showOutsideSB = Property.of(new ArrayList<>());
@Expose
@ConfigOption(name = "Exp Bottles", desc = "Hide all the experience orbs lying on the ground.")
@@ -216,6 +228,15 @@ public class MiscConfig {
@FeatureToggle
public boolean fixNeuHeavyPearls = true;
+ @Expose
+ @ConfigOption(
+ name = "Fix Patcher Lines",
+ desc = "Suggest in chat to disable Patcher's `parallax fix` that breaks SkyHanni's line from middle of player to somewhere else."
+ )
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean fixPatcherLines = true;
+
@Expose
@ConfigOption(
name = "Time In Limbo",
@@ -294,4 +315,10 @@ public class MiscConfig {
@ConfigEditorBoolean
@FeatureToggle
public boolean maintainGameVolume = false;
+
+ @Expose
+ @ConfigOption(name = "SkyHanni User Luck", desc = "Shows SkyHanni User Luck in the SkyBlock Stats.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean userluckEnabled = true;
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/PotionEffectsConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/PotionEffectsConfig.java
index 6ead171864ad..34af9fbf208f 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/misc/PotionEffectsConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/PotionEffectsConfig.java
@@ -4,6 +4,7 @@
import at.hannibal2.skyhanni.config.core.config.Position;
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigLink;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
@@ -20,7 +21,31 @@ public class PotionEffectsConfig {
@FeatureToggle
public boolean nonGodPotEffectShowMixins = false;
+ @Expose
+ @ConfigOption(name = "Expire Warning", desc = "Sends a title when one of the Non God Pot Effects is expiring.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean expireWarning = false;
+
+ @Expose
+ @ConfigOption(name = "Expire Sound", desc = "Makes a sound when one of the Non God Pot Effects is expiring.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean expireSound = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Expire Warning Time",
+ desc = "Change the time in seconds before the potion expries to warn you.")
+ @ConfigEditorSlider(
+ minValue = 30,
+ maxValue = 300,
+ minStep = 5
+ )
+ public int expireWarnTime = 30;
+
@Expose
@ConfigLink(owner = PotionEffectsConfig.class, field = "nonGodPotEffectDisplay")
+ // TODO rename position
public Position nonGodPotEffectPos = new Position(10, 10, false, true);
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/misc/RemindersConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/misc/RemindersConfig.java
new file mode 100644
index 000000000000..fb7a9b785db8
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/misc/RemindersConfig.java
@@ -0,0 +1,21 @@
+package at.hannibal2.skyhanni.config.features.misc;
+
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class RemindersConfig {
+ @Expose
+ @ConfigOption(name = "Auto Delete Reminders", desc = "Automatically deletes reminders after they have been shown once.")
+ @ConfigEditorBoolean
+ public boolean autoDeleteReminders = false;
+
+ @Expose
+ @ConfigOption(
+ name = "Reminder Interval",
+ desc = "The interval in minutes in which reminders are shown again, after they have been shown once."
+ )
+ @ConfigEditorSlider(minValue = 0f, maxValue = 60f, minStep = 1f)
+ public float interval = 5f;
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java
index 5bd3ec8fa4ba..96c721539c1c 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/EnigmaSoulConfig.java
@@ -4,16 +4,25 @@
import com.google.gson.annotations.Expose;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorColour;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorInfoText;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
public class EnigmaSoulConfig {
@Expose
- @ConfigOption(name = "Enabled", desc = "Click on Enigma Souls in Rift Guides to highlight their location.")
+ @ConfigOption(name = "Enabled", desc = "Click on an Enigma Soul in §eRift Guide -> Area -> Enigma Souls §7to highlight their location.")
@ConfigEditorBoolean
@FeatureToggle
public boolean enabled = true;
+ @ConfigOption(
+ name = "§aRift Guide",
+ desc = "Type §e/riftguide §7in chat or navigate through the SkyBlock Menu to open the §aRift Guide§7. " +
+ "Complete the first quest in the Rift to unlock this Hypixel feature."
+ )
+ @ConfigEditorInfoText
+ public String tutorialHowToOpenRiftGuide = "";
+
@Expose
@ConfigOption(name = "Color", desc = "Color of the Enigma Souls.")
@ConfigEditorColour
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftConfig.java
index 147709752f8a..9c24e810fc4c 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftConfig.java
@@ -31,7 +31,7 @@ public class RiftConfig {
public RiftAreasConfig area = new RiftAreasConfig();
@Expose
- @Category(name = "Motes", desc = "Motes Sell Price")
+ @Category(name = "Motes", desc = "")
public MotesConfig motes = new MotesConfig();
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftTimerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftTimerConfig.java
index 679a01aa32db..c2db26b8e176 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftTimerConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/RiftTimerConfig.java
@@ -30,4 +30,10 @@ public class RiftTimerConfig {
@ConfigLink(owner = RiftTimerConfig.class, field = "enabled")
public Position timerPosition = new Position(10, 10, false, true);
+ @Expose
+ @ConfigOption(name = "Nametag Format", desc = "Format the remaining rift time for other players in their nametag.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean nametag = true;
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/CraftingRoomConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/CraftingRoomConfig.java
new file mode 100644
index 000000000000..e1bc145c3521
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/CraftingRoomConfig.java
@@ -0,0 +1,31 @@
+package at.hannibal2.skyhanni.config.features.rift.area.mirrorverse;
+
+import at.hannibal2.skyhanni.config.FeatureToggle;
+import com.google.gson.annotations.Expose;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
+
+public class CraftingRoomConfig {
+
+ @Expose
+ @ConfigOption(name = "Enabled", desc = "Show an holographic version of the mob on your side of the craft room.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean enabled = true;
+
+ @Expose
+ @ConfigOption(name = "Show Name", desc = "Show the name of the mob.")
+ @ConfigEditorBoolean
+ public boolean showName = true;
+
+ @Expose
+ @ConfigOption(name = "Show Health", desc = "Show the health of the mob.")
+ @ConfigEditorBoolean
+ public boolean showHealth = true;
+
+ @Expose
+ @ConfigOption(name = "Hide Players", desc = "Hide other players in the room.")
+ @ConfigEditorBoolean
+ public boolean hidePlayers = true;
+
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/MirrorVerseConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/MirrorVerseConfig.java
index c1fedf3a4f22..0ab546ae8ab2 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/MirrorVerseConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/area/mirrorverse/MirrorVerseConfig.java
@@ -7,16 +7,25 @@
public class MirrorVerseConfig {
+ // Four Levers
+
@ConfigOption(name = "Lava Maze", desc = "")
@Accordion
@Expose
public LavaMazeConfig lavaMazeConfig = new LavaMazeConfig();
- @ConfigOption(name = "Upside Down Parkour", desc = "")
+ @ConfigOption(name = "Crafting Room", desc = "")
+ @Accordion
+ @Expose
+ public CraftingRoomConfig craftingRoom = new CraftingRoomConfig();
+
+ @ConfigOption(name = "Upside-Down Parkour", desc = "")
@Accordion
@Expose
public UpsideDownParkourConfig upsideDownParkour = new UpsideDownParkourConfig();
+ // Red-Green Puzzle
+
@ConfigOption(name = "Dance Room Helper", desc = "")
@Accordion
@Expose
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/rift/motes/MotesConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/rift/motes/MotesConfig.java
index d9e45f8fec4d..e02b7e589362 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/rift/motes/MotesConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/rift/motes/MotesConfig.java
@@ -20,6 +20,12 @@ public class MotesConfig {
@ConfigEditorSlider(minStep = 1, minValue = 0, maxValue = 5)
public int burgerStacks = 0;
+ @Expose
+ @ConfigOption(name = "Motes per Session", desc = "Show how many motes you got this session when leaving the rift.")
+ @ConfigEditorBoolean
+ @FeatureToggle
+ public boolean motesPerSession = true;
+
@Expose
@ConfigOption(name = "Inventory Value", desc = "")
@Accordion
diff --git a/src/main/java/at/hannibal2/skyhanni/config/features/slayer/SlayerConfig.java b/src/main/java/at/hannibal2/skyhanni/config/features/slayer/SlayerConfig.java
index 74f61be7f9a9..fedace9995e3 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/features/slayer/SlayerConfig.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/features/slayer/SlayerConfig.java
@@ -8,6 +8,7 @@
import io.github.notenoughupdates.moulconfig.annotations.Accordion;
import io.github.notenoughupdates.moulconfig.annotations.Category;
import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorBoolean;
+import io.github.notenoughupdates.moulconfig.annotations.ConfigEditorSlider;
import io.github.notenoughupdates.moulconfig.annotations.ConfigOption;
public class SlayerConfig {
@@ -59,6 +60,11 @@ public class SlayerConfig {
@FeatureToggle
public boolean slayerMinibossLine = false;
+ @Expose
+ @ConfigOption(name = "Line to Miniboss Width", desc = "The width of the line pointing to every Slayer Mini-Boss around you.")
+ @ConfigEditorSlider(minStep = 1, minValue = 1, maxValue = 10)
+ public int slayerMinibossLineWidth = 3;
+
@Expose
@ConfigOption(name = "Hide Mob Names", desc = "Hide the name of the mobs you need to kill in order for the Slayer boss to spawn. Exclude mobs that are damaged, corrupted, runic or semi rare.")
@ConfigEditorBoolean
diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
index 39ce4402b1a1..bb023f230d95 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/storage/ProfileSpecificStorage.java
@@ -13,6 +13,7 @@
import at.hannibal2.skyhanni.features.event.diana.DianaProfitTracker;
import at.hannibal2.skyhanni.features.event.diana.MythologicalCreatureTracker;
import at.hannibal2.skyhanni.features.event.hoppity.HoppityCollectionStats;
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType;
import at.hannibal2.skyhanni.features.event.jerry.frozentreasure.FrozenTreasureTracker;
import at.hannibal2.skyhanni.features.fame.UpgradeReminder;
import at.hannibal2.skyhanni.features.fishing.tracker.FishingProfitTracker;
@@ -39,6 +40,7 @@
import at.hannibal2.skyhanni.features.skillprogress.SkillType;
import at.hannibal2.skyhanni.features.slayer.SlayerProfitTracker;
import at.hannibal2.skyhanni.utils.GenericWrapper;
+import at.hannibal2.skyhanni.utils.LorenzRarity;
import at.hannibal2.skyhanni.utils.LorenzVec;
import at.hannibal2.skyhanni.utils.NEUInternalName;
import at.hannibal2.skyhanni.utils.SimpleTimeMark;
@@ -532,7 +534,7 @@ public static class MiningConfig {
public HotmTree hotmTree = new HotmTree();
@Expose
- public Map powder = new HashMap<>();
+ public Map powder = new HashMap<>();
public static class PowderStorage {
@@ -666,4 +668,42 @@ public static class WardrobeStorage {
@Expose
public UpgradeReminder.CommunityShopUpgrade communityShopProfileUpgrade = null;
+
+ @Expose
+ @Nullable
+ public Integer abiphoneContactAmount = null;
+
+ @Expose
+ public Map hoppityEventStats = new HashMap<>();
+
+ public static class HoppityEventStats {
+ @Expose
+ public Map mealsFound = new HashMap<>();
+
+ @Expose
+ public Map rabbitsFound = new HashMap<>();
+
+ public static class RabbitData {
+ @Expose
+ public int uniques = 0;
+
+ @Expose
+ public int dupes = 0;
+
+ @Expose
+ public int strays = 0;
+ }
+
+ @Expose
+ public long dupeChocolateGained = 0;
+
+ @Expose
+ public long strayChocolateGained = 0;
+
+ @Expose
+ public long millisInCf = 0;
+
+ @Expose
+ public boolean summarized = false;
+ }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/config/storage/Storage.java b/src/main/java/at/hannibal2/skyhanni/config/storage/Storage.java
index c37ba1dd0993..3b75a64eebbf 100644
--- a/src/main/java/at/hannibal2/skyhanni/config/storage/Storage.java
+++ b/src/main/java/at/hannibal2/skyhanni/config/storage/Storage.java
@@ -1,5 +1,6 @@
package at.hannibal2.skyhanni.config.storage;
+import at.hannibal2.skyhanni.features.misc.reminders.Reminder;
import at.hannibal2.skyhanni.features.misc.visualwords.VisualWord;
import at.hannibal2.skyhanni.utils.LorenzVec;
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker;
@@ -45,9 +46,13 @@ public class Storage {
@Expose
public Map players = new HashMap<>();
+ // TODO this should get moved into player specific
@Expose
public String currentFameRank = "New player";
@Expose
public List blacklistedUsers = new ArrayList<>();
+
+ @Expose
+ public Map reminders = new HashMap<>();
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt
index 72928fd25cd2..e9e58fa463d3 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/BitsAPI.kt
@@ -60,7 +60,7 @@ object BitsAPI {
// Scoreboard patterns
val bitsScoreboardPattern by bitsDataGroup.pattern(
"scoreboard",
- "^Bits: §b(?[\\d,.]+).*$"
+ "^Bits: §b(?[\\d,.]+).*$",
)
// Chat patterns
@@ -68,17 +68,17 @@ object BitsAPI {
private val bitsFromFameRankUpChatPattern by bitsChatGroup.pattern(
"rankup.bits",
- "§eYou gained §3(?.*) Bits Available §ecompounded from all your §epreviously eaten §6cookies§e! Click here to open §6cookie menu§e!"
+ "§eYou gained §3(?.*) Bits Available §ecompounded from all your §epreviously eaten §6cookies§e! Click here to open §6cookie menu§e!",
)
private val fameRankUpPattern by bitsChatGroup.pattern(
"rankup.rank",
- "[§\\w\\s]+FAME RANK UP (?:§.)+(?.*)"
+ "[§\\w\\s]+FAME RANK UP (?:§.)+(?.*)",
)
private val boosterCookieAte by bitsChatGroup.pattern(
"boostercookieate",
- "§eYou consumed a §6Booster Cookie§e!.*"
+ "§eYou consumed a §6Booster Cookie§e!.*",
)
// GUI patterns
@@ -86,12 +86,12 @@ object BitsAPI {
private val bitsAvailableMenuPattern by bitsGuiGroup.pattern(
"availablemenu",
- "§7Bits Available: §b(?[\\d,]+)(§3.+)?"
+ "§7Bits Available: §b(?[\\d,]+)(§3.+)?",
)
private val fameRankSbMenuPattern by bitsGuiGroup.pattern(
"sbmenufamerank",
- "§7Your rank: §e(?.*)"
+ "§7Your rank: §e(?.*)",
)
/**
@@ -99,47 +99,47 @@ object BitsAPI {
*/
private val cookieDurationPattern by bitsGuiGroup.pattern(
"cookieduration",
- "\\s*§7Duration: §a(?.*)"
+ "\\s*§7Duration: §a(?.*)",
)
private val noCookieActiveSBMenuPattern by bitsGuiGroup.pattern(
"sbmenunocookieactive",
- " §7Status: §cNot active!"
+ " §7Status: §cNot active!",
)
private val noCookieActiveCookieMenuPattern by bitsGuiGroup.pattern(
"cookiemenucookieactive",
- "(§7§cYou do not currently have a|§cBooster Cookie active!)"
+ "(§7§cYou do not currently have a|§cBooster Cookie active!)",
)
private val fameRankCommunityShopPattern by bitsGuiGroup.pattern(
"communityshopfamerank",
- "§7Fame Rank: §e(?.*)"
+ "§7Fame Rank: §e(?.*)",
)
private val bitsGuiNamePattern by bitsGuiGroup.pattern(
"mainmenuname",
- "^SkyBlock Menu$"
+ "^SkyBlock Menu$",
)
private val cookieGuiStackPattern by bitsGuiGroup.pattern(
"mainmenustack",
- "^§6Booster Cookie$"
+ "^§6Booster Cookie$",
)
private val bitsStackPattern by bitsGuiGroup.pattern(
"bitsstack",
- "§bBits"
+ "§bBits",
)
private val fameRankGuiNamePattern by bitsGuiGroup.pattern(
"famerankmenuname",
- "^(Community Shop|Booster Cookie)$"
+ "^(Community Shop|Booster Cookie)$",
)
private val fameRankGuiStackPattern by bitsGuiGroup.pattern(
"famerankmenustack",
- "^(§aCommunity Shop|§eFame Rank)$"
+ "^(§aCommunity Shop|§eFame Rank)$",
)
@SubscribeEvent
@@ -187,14 +187,14 @@ object BitsAPI {
"FameRank $rank not found",
"Rank" to rank,
"Message" to message,
- "FameRanks" to FameRanks.fameRanks
+ "FameRanks" to FameRanks.fameRanks,
)
return
}
boosterCookieAte.matchMatcher(message) {
- bitsAvailable += (defaultCookieBits * (currentFameRank?.bitsMultiplier ?: return)).toInt()
+ bitsAvailable += bitsPerCookie()
val cookieTime = cookieBuffTime
cookieBuffTime = if (cookieTime == null) SimpleTimeMark.now() + 4.days else cookieTime + 4.days
sendBitsAvailableGainedEvent()
@@ -203,6 +203,8 @@ object BitsAPI {
}
}
+ fun bitsPerCookie(): Int = (defaultCookieBits * (currentFameRank?.bitsMultiplier ?: 1.0)).toInt()
+
@SubscribeEvent
fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
if (!isEnabled()) return
@@ -258,7 +260,7 @@ object BitsAPI {
"FameRank $rank not found",
"Rank" to rank,
"Lore" to fameRankStack.getLore(),
- "FameRanks" to FameRanks.fameRanks
+ "FameRanks" to FameRanks.fameRanks,
)
continue@line
@@ -273,7 +275,7 @@ object BitsAPI {
"FameRank $rank not found",
"Rank" to rank,
"Lore" to fameRankStack.getLore(),
- "FameRanks" to FameRanks.fameRanks
+ "FameRanks" to FameRanks.fameRanks,
)
continue@line
diff --git a/src/main/java/at/hannibal2/skyhanni/data/ClickedBlockType.kt b/src/main/java/at/hannibal2/skyhanni/data/ClickedBlockType.kt
new file mode 100644
index 000000000000..e69c9b5160d8
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/ClickedBlockType.kt
@@ -0,0 +1,8 @@
+package at.hannibal2.skyhanni.data
+
+enum class ClickedBlockType {
+ LEVER,
+ CHEST,
+ TRAPPED_CHEST,
+ WITHER_ESSENCE
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/EntityData.kt b/src/main/java/at/hannibal2/skyhanni/data/EntityData.kt
index c4e5fd7e76db..d2f81a6d8191 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/EntityData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/EntityData.kt
@@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.events.entity.EntityDisplayNameEvent
+import at.hannibal2.skyhanni.events.entity.EntityHealthDisplayEvent
import at.hannibal2.skyhanni.events.minecraft.packet.PacketReceivedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.EntityUtils
@@ -32,6 +33,7 @@ object EntityData {
private val maxHealthMap = mutableMapOf()
private val nametagCache = TimeLimitedCache(50.milliseconds)
+ private val healthDisplayCache = TimeLimitedCache(50.milliseconds)
@SubscribeEvent
fun onTick(event: LorenzTickEvent) {
@@ -103,4 +105,12 @@ object EntityData {
event.postAndCatch()
event.chatComponent
}
+
+ @JvmStatic
+ fun getHealthDisplay(text: String) = healthDisplayCache.getOrPut(text) {
+ val event = EntityHealthDisplayEvent(text)
+ event.postAndCatch()
+ event.text
+ }
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/FixedRateTimerManager.kt b/src/main/java/at/hannibal2/skyhanni/data/FixedRateTimerManager.kt
index 4d825b2e9b9d..36dc3c6f0b24 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/FixedRateTimerManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/FixedRateTimerManager.kt
@@ -2,8 +2,8 @@ package at.hannibal2.skyhanni.data
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.LorenzUtils
-import net.minecraft.client.Minecraft
import kotlin.concurrent.fixedRateTimer
@SkyHanniModule
@@ -12,8 +12,8 @@ object FixedRateTimerManager {
init {
fixedRateTimer(name = "skyhanni-fixed-rate-timer-manager", period = 1000L) {
- Minecraft.getMinecraft().addScheduledTask {
- if (!LorenzUtils.onHypixel) return@addScheduledTask
+ DelayedRun.onThread.execute {
+ if (!LorenzUtils.onHypixel) return@execute
SecondPassedEvent(totalSeconds).postAndCatch()
totalSeconds++
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt b/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt
index 0f53a3e0cea1..e6dfa7671529 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/GuiEditManager.kt
@@ -78,14 +78,14 @@ object GuiEditManager {
}
@JvmStatic
- fun add(position: Position, posLabel: String, x: Int, y: Int) {
+ fun add(position: Position, posLabel: String, width: Int, height: Int) {
var name = position.internalName
if (name == null) {
name = if (posLabel == "none") "none " + UUID.randomUUID() else posLabel
position.internalName = name
}
currentPositions[name] = position
- currentBorderSize[posLabel] = Pair(x, y)
+ currentBorderSize[posLabel] = Pair(width, height)
}
private var lastHotkeyReminded = SimpleTimeMark.farPast()
diff --git a/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt b/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt
index e82ddd4aebea..0392eab25af8 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/HotmData.kt
@@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.api.HotmAPI.MayhemPerk
import at.hannibal2.skyhanni.api.HotmAPI.SkymallPerk
import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage
import at.hannibal2.skyhanni.data.jsonobjects.local.HotmTree
+import at.hannibal2.skyhanni.data.model.TabWidget
import at.hannibal2.skyhanni.events.DebugDataCollectEvent
import at.hannibal2.skyhanni.events.InventoryCloseEvent
import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
@@ -12,6 +13,7 @@ import at.hannibal2.skyhanni.events.IslandChangeEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.ProfileJoinEvent
import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent
+import at.hannibal2.skyhanni.events.WidgetUpdateEvent
import at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardPattern
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.test.command.ErrorManager
@@ -23,7 +25,6 @@ import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.formatLong
import at.hannibal2.skyhanni.utils.RegexUtils.indexOfFirstMatch
import at.hannibal2.skyhanni.utils.RegexUtils.matchFirst
@@ -475,6 +476,15 @@ enum class HotmData(
"§b§lMAYHEM! §r§7(?.*)",
)
+ /**
+ * REGEX-TEST: Mithril: §r§299,918
+ * REGEX-TEST: Gemstone: §r§d37,670
+ */
+ private val powderPattern by patternGroup.pattern(
+ "widget.powder",
+ "\\s*(?\\w+): (?:§.)+(?[\\d,.]+)"
+ )
+
var inInventory = false
var tokens: Int
@@ -493,7 +503,7 @@ enum class HotmData(
init {
entries.forEach { it.guiNamePattern }
- HotmAPI.Powder.entries.forEach {
+ HotmAPI.PowderType.entries.forEach {
it.heartPattern
it.resetPattern
}
@@ -527,7 +537,7 @@ enum class HotmData(
it.rawLevel = 0
it.enabled = false
it.isUnlocked = false
- HotmAPI.Powder.entries.forEach { it.setCurrent(it.getTotal()) }
+ HotmAPI.PowderType.entries.forEach { it.setCurrent(it.getTotal()) }
availableTokens = tokens
}
@@ -585,7 +595,7 @@ enum class HotmData(
if (isHeartItem) { // Reset on the heart Item to remove duplication
tokens = 0
availableTokens = 0
- HotmAPI.Powder.entries.forEach { it.reset() }
+ HotmAPI.PowderType.entries.forEach { it.reset() }
heartItem = this
}
@@ -595,7 +605,7 @@ enum class HotmData(
lore@ for (line in lore) {
- HotmAPI.Powder.entries.forEach {
+ HotmAPI.PowderType.entries.forEach {
it.pattern(isHeartItem).matchMatcher(line) {
val powder = group("powder").replace(",", "").toLong()
if (isHeartItem) {
@@ -654,13 +664,12 @@ enum class HotmData(
if (!LorenzUtils.inSkyBlock) return
event.scoreboard.matchFirst(ScoreboardPattern.powderPattern) {
- val type = HotmAPI.Powder.entries.firstOrNull { it.displayName == group("type") } ?: return
+ val type = HotmAPI.PowderType.entries.firstOrNull { it.displayName == group("type") } ?: return
val amount = group("amount").formatLong()
val difference = amount - type.getCurrent()
if (difference > 0) {
type.gain(difference)
- ChatUtils.debug("Gained §a${difference.addSeparators()} §e${type.displayName} Powder")
}
}
}
@@ -686,6 +695,22 @@ enum class HotmData(
}
}
+ @SubscribeEvent
+ fun onWidgetUpdate(event: WidgetUpdateEvent) {
+ if (!event.isWidget(TabWidget.POWDER)) return
+ event.lines.forEach {
+ powderPattern.matchMatcher(it) {
+ val type = HotmAPI.PowderType.entries.firstOrNull { it.displayName == group("type") } ?: return
+ val amount = group("amount").replace(",", "").toLong()
+ val difference = amount - type.getCurrent()
+
+ if (difference > 0) {
+ type.gain(difference)
+ }
+ }
+ }
+ }
+
@SubscribeEvent
fun onChat(event: LorenzChatEvent) {
if (!LorenzUtils.inSkyBlock) return
@@ -733,7 +758,7 @@ enum class HotmData(
@SubscribeEvent
fun onProfileSwitch(event: ProfileJoinEvent) {
- HotmAPI.Powder.entries.forEach {
+ HotmAPI.PowderType.entries.forEach {
if (it.getStorage() == null) {
ProfileStorageData.profileSpecific?.mining?.powder?.put(
it,
@@ -748,7 +773,7 @@ enum class HotmData(
event.title("HotM")
event.addIrrelevant {
add("Tokens : $availableTokens/$tokens")
- HotmAPI.Powder.entries.forEach {
+ HotmAPI.PowderType.entries.forEach {
add("${it.displayName} Powder: ${it.getCurrent()}/${it.getTotal()}")
}
add("Ability: ${HotmAPI.activeMiningAbility?.printName}")
diff --git a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
index 14aa579cae1a..0cd806f577c7 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/HypixelData.kt
@@ -318,7 +318,7 @@ object HypixelData {
if (LorenzUtils.onHypixel && LorenzUtils.inSkyBlock) {
loop@ for (line in ScoreboardData.sidebarLinesFormatted) {
skyblockAreaPattern.matchMatcher(line) {
- val originalLocation = group("area")
+ val originalLocation = group("area").removeColor()
skyBlockArea = LocationFixData.fixLocation(skyBlockIsland) ?: originalLocation
skyBlockAreaWithSymbol = line.trim()
break@loop
diff --git a/src/main/java/at/hannibal2/skyhanni/data/ItemAddManager.kt b/src/main/java/at/hannibal2/skyhanni/data/ItemAddManager.kt
index 26658eee7cc9..4906f5f80623 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/ItemAddManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/ItemAddManager.kt
@@ -25,6 +25,7 @@ object ItemAddManager {
enum class Source {
ITEM_ADD,
SACKS,
+ COMMAND,
}
private val ARCHFIEND_DICE = "ARCHFIEND_DICE".asInternalName()
diff --git a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt
index 53539adb4a07..2f89b340b656 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/MayorAPI.kt
@@ -24,6 +24,7 @@ import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.SimpleTimeMark.Companion.asTimeMark
@@ -63,17 +64,18 @@ object MayorAPI {
/**
* REGEX-TEST: Calendar and Events
*/
- private val calendarGuiPattern by group.pattern(
+ val calendarGuiPattern by group.pattern(
"calendar.gui",
"Calendar and Events",
)
/**
* REGEX-TEST: §dMayor Jerry
+ * REGEX-TEST: §cMayor Aatrox
*/
- private val jerryHeadPattern by group.pattern(
- "jerry.head",
- "§dMayor Jerry",
+ private val mayorHeadPattern by group.pattern(
+ "mayor.head",
+ "§.Mayor (?.*)",
)
/**
@@ -152,6 +154,7 @@ object MayorAPI {
if (electionOverPattern.matches(event.message)) {
lastMayor = currentMayor
currentMayor = Mayor.UNKNOWN
+ currentMinister = null
}
}
@@ -161,7 +164,11 @@ object MayorAPI {
if (!calendarGuiPattern.matches(event.inventoryName)) return
- val stack: ItemStack = event.inventoryItems.values.firstOrNull { jerryHeadPattern.matches(it.displayName) } ?: return
+ val stack: ItemStack = event.inventoryItems.values.firstOrNull {
+ mayorHeadPattern.matchMatcher(it.displayName) {
+ group("name") == "Jerry"
+ } ?: false
+ } ?: return
val perk = stack.getLore().nextAfter({ perkpocalypsePerksPattern.matches(it) }) ?: return
// This is the first Perk of the Perkpocalypse Mayor
@@ -218,7 +225,7 @@ object MayorAPI {
val currentMayorName = data.mayor.name
if (lastMayor?.name != currentMayorName) {
currentMayor = setAssumeMayorJson(currentMayorName, data.mayor.perks)
- currentMinister = setAssumeMayorJson(data.mayor.minister.name, data.mayor.minister.perk)
+ currentMinister = data.mayor.minister?.let { setAssumeMayorJson(it.name, listOf(it.perk)) }
}
}
}
@@ -247,7 +254,7 @@ object MayorAPI {
event.addIrrelevant {
add("Current Mayor: ${currentMayor?.name ?: "Unknown"}")
add("Active Perks: ${currentMayor?.activePerks}")
- add("Last Update: $lastUpdate (${lastUpdate.passedSince()} ago)")
+ add("Last Update: ${lastUpdate.formattedDate("EEEE, MMM d h:mm a")} (${lastUpdate.passedSince()} ago)")
add("Time Till Next Mayor: ${nextMayorTimestamp.timeUntil()}")
add("Current Minister: ${currentMinister?.name ?: "Unknown"}")
add("Current Minister Perk: ${currentMinister?.activePerks}")
diff --git a/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt b/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt
index 975282a3abc0..78647944c9a0 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/Mayors.kt
@@ -45,10 +45,10 @@ enum class Mayor(
FINNEGAN(
"Finnegan",
"§c",
- Perk.FARMING_SIMULATOR,
Perk.PELT_POCALYPSE,
Perk.GOATED,
Perk.BLOOMING_BUSINESS,
+ Perk.PEST_ERADICATOR,
),
FOXY(
"Foxy",
@@ -93,7 +93,7 @@ enum class Mayor(
"Derpy",
"§d",
Perk.TURBO_MINIONS,
- Perk.AH_TAX,
+ Perk.QUAD_TAXES,
Perk.DOUBLE_MOBS_HP,
Perk.MOAR_SKILLZ,
),
@@ -132,8 +132,12 @@ enum class Mayor(
)
return null
}
- val perks = perksJson.mapNotNull { perk ->
- Perk.entries.firstOrNull { it.perkName == perk.renameIfFoxyExtraEventPerkFound() }
+
+ val perks = perksJson.mapNotNull { perkJson ->
+ val perk = Perk.entries.firstOrNull { it.perkName == perkJson.renameIfFoxyExtraEventPerkFound() }
+ perk?.also {
+ it.description = perkJson.description
+ }
}
mayor.addPerks(perks)
@@ -149,17 +153,16 @@ enum class Mayor(
}
}
- private fun MayorPerk.renameIfFoxyExtraEventPerkFound(): String? {
+ private fun MayorPerk.renameIfFoxyExtraEventPerkFound(): String {
val foxyExtraEventPairs = mapOf(
"Spooky Festival" to "Extra Event (Spooky)",
"Mining Fiesta" to "Extra Event (Mining)",
"Fishing Festival" to "Extra Event (Fishing)",
)
- foxyExtraEventPattern.matchMatcher(this.description) {
- return foxyExtraEventPairs.entries.firstOrNull { it.key == group("event") }?.value
- }
- return this.name
+ return foxyExtraEventPattern.matchMatcher(this.description) {
+ foxyExtraEventPairs.entries.firstOrNull { it.key == group("event") }?.value
+ } ?: this.name
}
}
}
@@ -189,10 +192,10 @@ enum class Perk(val perkName: String) {
LONG_TERM_INVESTMENT("Long Term Investment"),
// Finnegan
- FARMING_SIMULATOR("Farming Simulator"),
PELT_POCALYPSE("Pelt-pocalypse"),
GOATED("GOATed"),
BLOOMING_BUSINESS("Blooming Business"),
+ PEST_ERADICATOR("Pest Eradicator"),
// Foxy
SWEET_BENEVOLENCE("Sweet Benevolence"),
@@ -224,14 +227,15 @@ enum class Perk(val perkName: String) {
// Derpy
TURBO_MINIONS("TURBO MINIONS!!!"),
- AH_TAX("MOAR TAX!!!"),
-
- // I don't know what the perk is actually gonna be named
+ QUAD_TAXES("QUAD TAXES!!!"),
DOUBLE_MOBS_HP("DOUBLE MOBS HP!!!"),
MOAR_SKILLZ("MOAR SKILLZ!!!"),
;
var isActive = false
+ var description = "§cDescription failed to load from the API."
+
+ override fun toString(): String = "$perkName: $description"
companion object {
fun getPerkFromName(name: String): Perk? = entries.firstOrNull { it.perkName == name }
diff --git a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt
index e97d25c52516..ecf90172dc79 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/MiningAPI.kt
@@ -164,7 +164,8 @@ object MiningAPI {
@SubscribeEvent
fun onBlockChange(event: ServerBlockChangeEvent) {
if (!inCustomMiningIsland()) return
- if (event.newState.block != Blocks.air) return
+ if (event.newState.block.let { it != Blocks.air && it != Blocks.bedrock }) return
+ if (event.oldState.block.let { it == Blocks.air || it == Blocks.bedrock }) return
if (event.oldState.block == Blocks.air) return
val pos = event.location
if (pos.distanceToPlayer() > 7) return
diff --git a/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt b/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt
index 6a855a06bcf6..0b4abf42586f 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/OwnInventoryData.kt
@@ -15,7 +15,9 @@ import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.ItemUtils.itemName
+import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
@@ -37,7 +39,7 @@ object OwnInventoryData {
private var dirty = false
private val sackToInventoryChatPattern by RepoPattern.pattern(
"data.owninventory.chat.movedsacktoinventory",
- "§aMoved §r§e\\d* (?.*)§r§a from your Sacks to your inventory."
+ "§aMoved §r§e\\d* (?.*)§r§a from your Sacks to your inventory.",
)
@HandleEvent(priority = HandleEvent.LOW, receiveCancelled = true, onlyOnSkyblock = true)
@@ -117,6 +119,46 @@ object OwnInventoryData {
@SubscribeEvent
fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) {
ignoreItem(500.milliseconds) { true }
+
+ val itemName = event.item?.name ?: return
+ checkAHMovements(itemName)
+ }
+
+ private fun checkAHMovements(itemName: String) {
+ val inventoryName = InventoryUtils.openInventoryName()
+
+ // cancel own auction
+ if (inventoryName.let { it == "BIN Auction View" || it == "Auction View" }) {
+ if (itemName == "§cCancel Auction") {
+ val item = InventoryUtils.getItemAtSlotIndex(13)
+ val internalName = item?.getInternalNameOrNull() ?: return
+ OwnInventoryData.ignoreItem(5.seconds, { it == internalName })
+ }
+ }
+
+ // bought item from bin ah
+ if (inventoryName == "Confirm Purchase" && itemName == "§aConfirm") {
+ val item = InventoryUtils.getItemAtSlotIndex(13)
+ val internalName = item?.getInternalNameOrNull() ?: return
+ OwnInventoryData.ignoreItem(5.seconds, { it == internalName })
+ }
+
+ // bought item from normal ah
+ if (inventoryName == "Auction View" && itemName == "§6Collect Auction") {
+ val item = InventoryUtils.getItemAtSlotIndex(13)
+ val internalName = item?.getInternalNameOrNull() ?: return
+ OwnInventoryData.ignoreItem(5.seconds, { it == internalName })
+ }
+
+ // collected all items in "own bins"
+ if (inventoryName == "Your Bids" && itemName == "§aClaim All") {
+ for (stack in InventoryUtils.getItemsInOpenChest().map { it.stack }) {
+ if (stack.getLore().any { it == "§7Status: §aSold!" || it == "7Status: §aEnded!" }) {
+ val internalName = stack.getInternalNameOrNull() ?: return
+ OwnInventoryData.ignoreItem(5.seconds, { it == internalName })
+ }
+ }
+ }
}
@SubscribeEvent
@@ -127,7 +169,7 @@ object OwnInventoryData {
}
}
- private fun ignoreItem(duration: Duration, condition: (NEUInternalName) -> Boolean) {
+ fun ignoreItem(duration: Duration, condition: (NEUInternalName) -> Boolean) {
ignoredItemsUntil.add(IgnoredItem(condition, SimpleTimeMark.now() + duration))
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt
index f066191699f2..2ee30c27e55a 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/PartyAPI.kt
@@ -21,68 +21,69 @@ object PartyAPI {
private val patternGroup = RepoPattern.group("data.party")
private val youJoinedPartyPattern by patternGroup.pattern(
"you.joined",
- "§eYou have joined (?.*)'s? §eparty!"
+ "§eYou have joined (?.*)'s? §eparty!",
)
private val othersJoinedPartyPattern by patternGroup.pattern(
"others.joined",
- "(?.*) §ejoined the party\\."
+ "(?.*) §ejoined the party\\.",
)
private val othersInThePartyPattern by patternGroup.pattern(
"others.inparty",
- "§eYou'll be partying with: (?.*)"
+ "§eYou'll be partying with: (?.*)",
)
private val otherLeftPattern by patternGroup.pattern(
"others.left",
- "(?.*) §ehas left the party\\."
+ "(?.*) §ehas left the party\\.",
)
private val otherKickedPattern by patternGroup.pattern(
"others.kicked",
- "(?.*) §ehas been removed from the party\\."
+ "(?.*) §ehas been removed from the party\\.",
)
private val otherOfflineKickedPattern by patternGroup.pattern(
"others.offline",
- "§eKicked (?.*) because they were offline\\."
+ "§eKicked (?.*) because they were offline\\.",
)
private val otherDisconnectedPattern by patternGroup.pattern(
"others.disconnect",
- "(?.*) §ewas removed from your party because they disconnected\\."
+ "(?.*) §ewas removed from your party because they disconnected\\.",
)
private val transferOnLeavePattern by patternGroup.pattern(
"others.transfer.leave",
- "The party was transferred to (?.*) because (?.*) left"
+ "The party was transferred to (?.*) because (?.*) left",
)
- private val transferVoluntaryPattern by patternGroup.pattern(
+ val transferVoluntaryPattern by patternGroup.pattern(
"others.transfer.voluntary",
- "The party was transferred to (?.*) by .*"
+ "The party was transferred to (?.*) by (?.*)",
)
private val disbandedPattern by patternGroup.pattern(
"others.disband",
- ".* §ehas disbanded the party!"
+ ".* §ehas disbanded the party!",
)
private val kickedPattern by patternGroup.pattern(
"you.kicked",
- "§eYou have been kicked from the party by .* §e"
+ "§eYou have been kicked from the party by .* §e",
)
private val partyMembersStartPattern by patternGroup.pattern(
"members.start",
- "§6Party Members \\(\\d+\\)"
+ "§6Party Members \\(\\d+\\)",
)
private val partyMemberListPattern by patternGroup.pattern(
"members.list.withkind",
- "Party (?Leader|Moderators|Members): (?.*)"
+ "Party (?Leader|Moderators|Members): (?.*)",
)
private val kuudraFinderJoinPattern by patternGroup.pattern(
"kuudrafinder.join",
- "§dParty Finder §f> (?.*?) §ejoined the group! \\(§[a-fA-F0-9]+Combat Level \\d+§e\\)"
+ "§dParty Finder §f> (?.*?) §ejoined the group! \\(§[a-fA-F0-9]+Combat Level \\d+§e\\)",
)
private val dungeonFinderJoinPattern by patternGroup.pattern(
"dungeonfinder.join",
- "§dParty Finder §f> (?.*?) §ejoined the dungeon group! \\(§[a-fA-F0-9].* Level \\d+§[a-fA-F0-9]\\)"
+ "§dParty Finder §f> (?.*?) §ejoined the dungeon group! \\(§[a-fA-F0-9].* Level \\d+§[a-fA-F0-9]\\)",
)
val partyMembers = mutableListOf()
var partyLeader: String? = null
+ var prevPartyLeader: String? = null
fun listMembers() {
val size = partyMembers.size
@@ -145,15 +146,15 @@ object PartyAPI {
// one member got removed
otherLeftPattern.matchMatcher(message) {
val name = group("name").cleanPlayerName()
- partyMembers.remove(name)
+ removeWithLeader(name)
}
otherKickedPattern.matchMatcher(message) {
val name = group("name").cleanPlayerName()
- partyMembers.remove(name)
+ removeWithLeader(name)
}
otherOfflineKickedPattern.matchMatcher(message) {
val name = group("name").cleanPlayerName()
- partyMembers.remove(name)
+ removeWithLeader(name)
}
otherDisconnectedPattern.matchMatcher(message) {
val name = group("name").cleanPlayerName()
@@ -166,6 +167,7 @@ object PartyAPI {
}
transferVoluntaryPattern.matchMatcher(message.removeColor()) {
partyLeader = group("newowner").cleanPlayerName()
+ prevPartyLeader = group("name").cleanPlayerName()
}
// party disbanded
@@ -201,6 +203,13 @@ object PartyAPI {
}
}
+ private fun removeWithLeader(name: String) {
+ partyMembers.remove(name)
+ if (name == prevPartyLeader) {
+ prevPartyLeader = null
+ }
+ }
+
private fun addPlayer(playerName: String) {
if (partyMembers.contains(playerName)) return
if (playerName == LorenzUtils.getPlayerName()) return
@@ -210,5 +219,6 @@ object PartyAPI {
private fun partyLeft() {
partyMembers.clear()
partyLeader = null
+ prevPartyLeader = null
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt b/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt
index 0ade6a946036..378fe03c6953 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/ScoreboardData.kt
@@ -6,7 +6,10 @@ import at.hannibal2.skyhanni.events.RawScoreboardUpdateEvent
import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent
import at.hannibal2.skyhanni.events.minecraft.packet.PacketReceivedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils.lastColorCode
+import at.hannibal2.skyhanni.utils.TimeUtils.format
import net.minecraft.client.Minecraft
import net.minecraft.network.play.server.S3CPacketUpdateScore
import net.minecraft.network.play.server.S3EPacketTeams
@@ -22,7 +25,7 @@ object ScoreboardData {
private var sidebarLines: List = emptyList() // TODO rename to raw
var sidebarLinesRaw: List = emptyList() // TODO delete
- var objectiveTitle = ""
+ val objectiveTitle: String get() = Minecraft.getMinecraft().theWorld?.scoreboard?.getObjectiveInDisplaySlot(1)?.displayName ?: ""
private var dirty = false
@@ -33,10 +36,32 @@ object ScoreboardData {
val start = split[0]
var end = if (split.size > 1) split[1] else ""
+ /**
+ * If the line is split into two parts, we need to remove the color code prefixes from the end part
+ * to prevent the color from being applied to the start of `end`, which would cause the color to be
+ * duplicated in the final output.
+ *
+ * This fucks up different Regex checks if not working correctly, like here:
+ * ```
+ * Pattern: '§8- (§.)+[\w\s]+Dragon§a [\w,.]+§.❤'
+ * Lines: - '§8- §c§aApex Dra§agon§a 486M§c❤'
+ * - '§8- §c§6Flame Dr§6agon§a 460M§c❤'
+ * ```
+ */
val lastColor = start.lastColorCode() ?: ""
- if (end.startsWith(lastColor)) {
- end = end.removePrefix(lastColor)
+ // Generate the list of color suffixes
+ val colorSuffixes = generateSequence(lastColor) { it.dropLast(2) }
+ .takeWhile { it.isNotEmpty() }
+ .toMutableList()
+
+ // Iterate through the colorSuffixes to remove matching prefixes from 'end'
+ for (suffix in colorSuffixes.toList()) {
+ if (end.startsWith(suffix)) {
+ end = end.removePrefix(suffix)
+ colorSuffixes.remove(suffix)
+ break
+ }
}
add(start + end)
@@ -48,19 +73,41 @@ object ScoreboardData {
if (event.packet is S3CPacketUpdateScore) {
if (event.packet.objectiveName == "update") {
dirty = true
+ monitor()
}
}
if (event.packet is S3EPacketTeams) {
if (event.packet.name.startsWith("team_")) {
dirty = true
+ monitor()
}
}
}
+ private var monitor = false
+ private var lastMonitorState = emptyList()
+ private var lastChangeTime = SimpleTimeMark.farPast()
+
+ private fun monitor() {
+ if (!monitor) return
+ val currentList = fetchScoreboardLines()
+ if (lastMonitorState != currentList) {
+ val time = lastChangeTime.passedSince()
+ lastChangeTime = SimpleTimeMark.now()
+ println("Scoreboard Monitor: (new change after ${time.format(showMilliSeconds = true)})")
+ for (s in currentList) {
+ println("'$s'")
+ }
+ }
+ lastMonitorState = currentList
+ println(" ")
+ }
+
@SubscribeEvent(priority = EventPriority.HIGHEST)
fun onTick(event: LorenzTickEvent) {
if (!dirty) return
dirty = false
+ monitor()
val list = fetchScoreboardLines().reversed()
val semiFormatted = list.map { cleanSB(it) }
@@ -77,6 +124,13 @@ object ScoreboardData {
}
}
+ fun toggleMonitor() {
+ monitor = !monitor
+ val action = if (monitor) "Enabled" else "Disabled"
+ ChatUtils.chat("$action scoreboard monitoring in the console.")
+
+ }
+
private fun cleanSB(scoreboard: String): String {
return scoreboard.toCharArray().filter { it.code in 21..126 || it.code == 167 }.joinToString(separator = "")
}
@@ -84,7 +138,6 @@ object ScoreboardData {
private fun fetchScoreboardLines(): List {
val scoreboard = Minecraft.getMinecraft().theWorld?.scoreboard ?: return emptyList()
val objective = scoreboard.getObjectiveInDisplaySlot(1) ?: return emptyList()
- objectiveTitle = objective.displayName
var scores = scoreboard.getSortedScores(objective)
val list = scores.filter { input: Score? ->
input != null && input.playerName != null && !input.playerName.startsWith("#")
diff --git a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
index 4f3b3713c770..0d91c7f62856 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/SlayerAPI.kt
@@ -47,9 +47,13 @@ object SlayerAPI {
val totalPrice = maxPrice * amount
val format = totalPrice.shortFormat()
- val priceFormat = " §7(§6$format coins§7)"
- "$amountFormat$displayName$priceFormat" to totalPrice
+ if (internalName == NEUInternalName.SKYBLOCK_COIN) {
+ "§6$format coins" to totalPrice
+ } else {
+ val priceFormat = " §7(§6$format coins§7)"
+ "$amountFormat$displayName$priceFormat" to totalPrice
+ }
}
@SubscribeEvent
diff --git a/src/main/java/at/hannibal2/skyhanni/data/TrackerManager.kt b/src/main/java/at/hannibal2/skyhanni/data/TrackerManager.kt
index 71b60d76ea35..c1a7a60c3f49 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/TrackerManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/TrackerManager.kt
@@ -3,8 +3,12 @@ package at.hannibal2.skyhanni.data
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.events.ConfigLoadEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.ItemAddEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.ConditionalUtils
+import at.hannibal2.skyhanni.utils.NEUInternalName
+import at.hannibal2.skyhanni.utils.NumberUtil.formatIntOrUserError
import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@@ -13,6 +17,7 @@ object TrackerManager {
private var hasChanged = false
var dirty = false
+ var commandEditTrackerSuccess = false
@SubscribeEvent
fun onConfigLoad(event: ConfigLoadEvent) {
@@ -36,4 +41,30 @@ object TrackerManager {
hasChanged = false
}
}
+
+ fun commandEditTracker(args: Array) {
+ if (args.size < 2) {
+ ChatUtils.userError("Usage: /shedittracker -
")
+ return
+ }
+
+ val amount = args.last().formatIntOrUserError() ?: return
+ if (amount == 0) {
+ ChatUtils.userError("Amount can not be zero!")
+ return
+ }
+
+ val rawName = args.dropLast(1).joinToString(" ")
+ val internalName = NEUInternalName.fromItemNameOrInternalName(rawName)
+ if (!internalName.isKnownItem()) {
+ ChatUtils.chat("No item found for '$rawName'!")
+ return
+ }
+
+ commandEditTrackerSuccess = false
+ ItemAddEvent(internalName, amount, ItemAddManager.Source.COMMAND).postAndCatch()
+ if (!commandEditTrackerSuccess) {
+ ChatUtils.userError("Could not edit the Item Tracker! Does this item belong to this tracker? Is the tracker active right now?")
+ }
+ }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/other/MayorJson.kt b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/other/MayorJson.kt
index f54c7daf4b73..272ed5591af3 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/other/MayorJson.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/jsonobjects/other/MayorJson.kt
@@ -11,7 +11,9 @@ data class MayorInfo(
@Expose val key: String,
@Expose val name: String,
@Expose val perks: List,
- @Expose val minister: Minister,
+ // Ministers won't exist,
+ // when the current mayor is a special mayor
+ @Expose val minister: Minister?,
@Expose val election: MayorElection,
)
@@ -30,7 +32,7 @@ data class MayorCandidate(
data class Minister(
@Expose val key: String,
@Expose val name: String,
- @Expose val perk: List,
+ @Expose val perk: MayorPerk,
)
data class MayorPerk(
diff --git a/src/main/java/at/hannibal2/skyhanni/data/mob/IslandExceptions.kt b/src/main/java/at/hannibal2/skyhanni/data/mob/IslandExceptions.kt
index eadfe8196854..2acbe3546117 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/mob/IslandExceptions.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/mob/IslandExceptions.kt
@@ -2,7 +2,6 @@ package at.hannibal2.skyhanni.data.mob
import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.data.mob.MobFilter.makeMobResult
-import at.hannibal2.skyhanni.features.dungeon.DungeonAPI
import at.hannibal2.skyhanni.utils.EntityUtils.cleanName
import at.hannibal2.skyhanni.utils.EntityUtils.isNPC
import at.hannibal2.skyhanni.utils.ItemUtils.getSkullTexture
@@ -37,201 +36,226 @@ object IslandExceptions {
baseEntity: EntityLivingBase,
armorStand: EntityArmorStand?,
nextEntity: EntityLivingBase?,
- ): MobData.MobResult? {
- return if (DungeonAPI.inDungeon()) {
- when {
- baseEntity is EntityZombie && armorStand != null && (armorStand.name == "§e﴾ §c§lThe Watcher§r§r §e﴿" || armorStand.name == "§3§lWatchful Eye§r") -> MobData.MobResult.found(
- MobFactories.special(baseEntity, armorStand.cleanName(), armorStand)
- )
+ ): MobData.MobResult? =
+ when (LorenzUtils.skyBlockIsland) {
+ IslandType.CATACOMBS -> dungeon(baseEntity, armorStand, nextEntity)
+ IslandType.PRIVATE_ISLAND -> privateIsland(armorStand, baseEntity)
+ IslandType.THE_RIFT -> theRift(baseEntity, nextEntity, armorStand)
+ IslandType.CRIMSON_ISLE -> crimsonIsle(baseEntity, armorStand, nextEntity)
+ IslandType.DEEP_CAVERNS -> deepCaverns(baseEntity)
+ IslandType.DWARVEN_MINES -> dwarvenMines(baseEntity)
+ IslandType.CRYSTAL_HOLLOWS -> crystalHollows(baseEntity, armorStand)
+ IslandType.HUB -> hub(baseEntity, armorStand, nextEntity)
+ IslandType.GARDEN -> garden(baseEntity)
+ IslandType.KUUDRA_ARENA -> kuudraArena(baseEntity, nextEntity)
+ IslandType.WINTER -> winterIsland(baseEntity)
- baseEntity is EntityCaveSpider -> MobUtils.getClosedArmorStand(baseEntity, 2.0).takeNonDefault()
- .makeMobResult { MobFactories.dungeon(baseEntity, it) }
+ else -> null
+ }
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "Shadow Assassin" -> MobUtils.getClosedArmorStandWithName(
- baseEntity,
- 3.0,
- "Shadow Assassin"
- ).makeMobResult { MobFactories.dungeon(baseEntity, it) }
+ private fun dungeon(
+ baseEntity: EntityLivingBase,
+ armorStand: EntityArmorStand?,
+ nextEntity: EntityLivingBase?,
+ ) = when {
+ baseEntity is EntityZombie && armorStand != null &&
+ (armorStand.name == "§e﴾ §c§lThe Watcher§r§r §e﴿" || armorStand.name == "§3§lWatchful Eye§r") ->
+ MobData.MobResult.found(
+ MobFactories.special(baseEntity, armorStand.cleanName(), armorStand),
+ )
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "The Professor" -> MobUtils.getArmorStand(
- baseEntity,
- 9
- ).makeMobResult { MobFactories.boss(baseEntity, it) }
-
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && (nextEntity is EntityGiantZombie || nextEntity == null) && baseEntity.name.contains(
- "Livid"
- ) -> MobUtils.getClosedArmorStandWithName(baseEntity, 6.0, "﴾ Livid")
- .makeMobResult { MobFactories.boss(baseEntity, it, overriddenName = "Real Livid") }
-
- baseEntity is EntityIronGolem && MobFilter.wokeSleepingGolemPattern.matches(
- armorStand?.name ?: ""
- ) -> MobData.MobResult.found(
- Mob(
- baseEntity,
- Mob.Type.DUNGEON,
- armorStand,
- "Sleeping Golem"
- )
- ) // Consistency fix
- else -> null
- }
- } else when (LorenzUtils.skyBlockIsland) {
- IslandType.PRIVATE_ISLAND -> when {
- armorStand?.isDefaultValue() != false -> if (baseEntity.getLorenzVec()
- .distanceChebyshevIgnoreY(LocationUtils.playerLocation()) < 15.0
- ) MobData.MobResult.found(MobFactories.minionMob(baseEntity)) else MobData.MobResult.notYetFound // TODO fix to always include Valid Mobs on Private Island
- else -> null
- }
-
- IslandType.THE_RIFT -> when {
- baseEntity is EntitySlime && nextEntity is EntitySlime -> MobData.MobResult.illegal// Bacte Tentacle
- baseEntity is EntitySlime && armorStand != null && armorStand.cleanName()
- .startsWith("﴾ [Lv10] B") -> MobData.MobResult.found(
- Mob(
- baseEntity,
- Mob.Type.BOSS,
- armorStand,
- name = "Bacte"
- )
- )
-
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "Branchstrutter " -> MobData.MobResult.found(
- Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Branchstrutter")
- )
-
- else -> null
- }
-
- IslandType.CRIMSON_ISLE -> when {
- baseEntity is EntitySlime && armorStand?.name == "§f§lCOLLECT!" -> MobData.MobResult.found(
- MobFactories.special(
- baseEntity,
- "Heavy Pearl"
- )
- )
-
- baseEntity is EntityPig && nextEntity is EntityPig -> MobData.MobResult.illegal // Matriarch Tongue
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "BarbarianGuard " -> MobData.MobResult.found(
- Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Barbarian Guard")
- )
-
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "MageGuard " -> MobData.MobResult.found(
- Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Mage Guard")
- )
-
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "Mage Outlaw" -> MobData.MobResult.found(
- Mob(baseEntity, Mob.Type.BOSS, armorStand, name = "Mage Outlaw")
- ) // fix for wierd name
- baseEntity is EntityPigZombie && baseEntity.inventory?.get(4)
- ?.getSkullTexture() == MobFilter.NPC_TURD_SKULL -> MobData.MobResult.found(
- Mob(
- baseEntity,
- Mob.Type.DISPLAY_NPC,
- name = "Turd"
- )
- )
-
- baseEntity is EntityOcelot -> if (MobFilter.createDisplayNPC(baseEntity)) MobData.MobResult.illegal else MobData.MobResult.notYetFound // Maybe a problem in the future
- else -> null
- }
-
- IslandType.DEEP_CAVERNS -> when {
- baseEntity is EntityCreeper && baseEntity.baseMaxHealth.derpy() == 120 -> MobData.MobResult.found(
- Mob(
- baseEntity,
- Mob.Type.BASIC,
- name = "Sneaky Creeper",
- levelOrTier = 3
- )
- )
-
- else -> null
- }
-
- IslandType.DWARVEN_MINES -> when {
- baseEntity is EntityCreeper && baseEntity.baseMaxHealth.derpy() == 1_000_000 -> MobData.MobResult.found(
- MobFactories.basic(baseEntity, "Ghost")
- )
-
- else -> null
- }
-
- IslandType.CRYSTAL_HOLLOWS -> when {
- baseEntity is EntityMagmaCube && armorStand != null && armorStand.cleanName() == "[Lv100] Bal ???❤" -> MobData.MobResult.found(
- Mob(baseEntity, Mob.Type.BOSS, armorStand, "Bal", levelOrTier = 100)
- )
-
- else -> null
- }
-
- IslandType.HUB -> when {
- baseEntity is EntityOcelot && armorStand?.isDefaultValue() == false && armorStand.name.startsWith("§8[§7Lv155§8] §cAzrael§r") -> MobUtils.getArmorStand(
- baseEntity,
- 1
- ).makeMobResult { MobFactories.basic(baseEntity, it) }
+ baseEntity is EntityCaveSpider -> MobUtils.getClosedArmorStand(baseEntity, 2.0).takeNonDefault()
+ .makeMobResult { MobFactories.dungeon(baseEntity, it) }
- baseEntity is EntityOcelot && (nextEntity is EntityOcelot || nextEntity == null) -> MobUtils.getArmorStand(
- baseEntity,
- 3
- ).makeMobResult { MobFactories.basic(baseEntity, it) }
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "Shadow Assassin" ->
+ MobUtils.getClosedArmorStandWithName(baseEntity, 3.0, "Shadow Assassin")
+ .makeMobResult { MobFactories.dungeon(baseEntity, it) }
- baseEntity is EntityOtherPlayerMP && (baseEntity.name == "Minos Champion" || baseEntity.name == "Minos Inquisitor" || baseEntity.name == "Minotaur ") && armorStand != null -> MobUtils.getArmorStand(
- baseEntity,
- 2
- ).makeMobResult { MobFactories.basic(baseEntity, it, listOf(armorStand)) }
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "The Professor" ->
+ MobUtils.getArmorStand(baseEntity, 9)
+ .makeMobResult { MobFactories.boss(baseEntity, it) }
- baseEntity is EntityZombie && armorStand?.isDefaultValue() == true && MobUtils.getNextEntity(
- baseEntity,
- 4
- )?.name?.startsWith("§e") == true -> petCareHandler(baseEntity)
-
- baseEntity is EntityZombie && armorStand != null && !armorStand.isDefaultValue() -> null // Impossible Rat
- baseEntity is EntityZombie -> ratHandler(baseEntity, nextEntity) // Possible Rat
- else -> null
- }
-
- IslandType.GARDEN -> when {
- baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() -> MobData.MobResult.found(
- Mob(
- baseEntity,
- Mob.Type.DISPLAY_NPC,
- name = baseEntity.cleanName()
- )
- )
-
- else -> null
- }
-
- IslandType.KUUDRA_ARENA -> when {
- baseEntity is EntityMagmaCube && nextEntity is EntityMagmaCube -> MobData.MobResult.illegal
- baseEntity is EntityZombie && nextEntity is EntityZombie -> MobData.MobResult.illegal
- baseEntity is EntityZombie && nextEntity is EntityGiantZombie -> MobData.MobResult.illegal
- else -> null
- }
-
- IslandType.WINTER -> when {
- baseEntity is EntityMagmaCube && MobFilter.jerryMagmaCubePattern.matches(
- MobUtils.getArmorStand(
- baseEntity,
- 2
- )?.name
- ) ->
- MobData.MobResult.found(
- Mob(
- baseEntity,
- Mob.Type.BOSS,
- MobUtils.getArmorStand(baseEntity, 2),
- "Jerry Magma Cube"
- )
- )
-
- else -> null
- }
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() &&
+ (nextEntity is EntityGiantZombie || nextEntity == null) &&
+ baseEntity.name.contains("Livid") -> MobUtils.getClosedArmorStandWithName(baseEntity, 6.0, "﴾ Livid")
+ .makeMobResult { MobFactories.boss(baseEntity, it, overriddenName = "Real Livid") }
+
+ baseEntity is EntityIronGolem && MobFilter.wokeSleepingGolemPattern.matches(armorStand?.name ?: "") ->
+ MobData.MobResult.found(Mob(baseEntity, Mob.Type.DUNGEON, armorStand, "Sleeping Golem")) // Consistency fix
+
+ else -> null
+ }
+
+ private fun privateIsland(
+ armorStand: EntityArmorStand?,
+ baseEntity: EntityLivingBase,
+ ) = when {
+ armorStand?.isDefaultValue() != false ->
+ if (baseEntity.getLorenzVec().distanceChebyshevIgnoreY(LocationUtils.playerLocation()) < 15.0) {
+ // TODO fix to always include Valid Mobs on Private Island
+ MobData.MobResult.found(MobFactories.minionMob(baseEntity))
+ } else MobData.MobResult.notYetFound
+
+ else -> null
+ }
+
+ private fun theRift(
+ baseEntity: EntityLivingBase,
+ nextEntity: EntityLivingBase?,
+ armorStand: EntityArmorStand?,
+ ) = when {
+ baseEntity is EntitySlime && nextEntity is EntitySlime -> MobData.MobResult.illegal// Bacte Tentacle
+ baseEntity is EntitySlime && armorStand != null && armorStand.cleanName().startsWith("﴾ [Lv10] B") ->
+ MobData.MobResult.found(Mob(baseEntity, Mob.Type.BOSS, armorStand, name = "Bacte"))
+
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "Branchstrutter " ->
+ MobData.MobResult.found(
+ Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Branchstrutter"),
+ )
+
+ else -> null
+ }
+
+ private fun crimsonIsle(
+ baseEntity: EntityLivingBase,
+ armorStand: EntityArmorStand?,
+ nextEntity: EntityLivingBase?,
+ ) = when {
+ baseEntity is EntitySlime && MobFilter.heavyPearlPattern.matches(armorStand?.name) ->
+ MobData.MobResult.found(
+ MobFactories.special(baseEntity, "Heavy Pearl"),
+ )
+
+ baseEntity is EntityPig && nextEntity is EntityPig -> MobData.MobResult.illegal // Matriarch Tongue
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "BarbarianGuard " ->
+ MobData.MobResult.found(
+ Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Barbarian Guard"),
+ )
+
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "MageGuard " ->
+ MobData.MobResult.found(
+ Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Mage Guard"),
+ )
+
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() && baseEntity.name == "Mage Outlaw" ->
+ // fix for wierd name
+ MobData.MobResult.found(Mob(baseEntity, Mob.Type.BOSS, armorStand, name = "Mage Outlaw"))
+
+ baseEntity is EntityPigZombie &&
+ baseEntity.inventory?.get(4)?.getSkullTexture() == MobFilter.NPC_TURD_SKULL ->
+ MobData.MobResult.found(Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = "Turd"))
+
+ baseEntity is EntityOcelot -> if (MobFilter.createDisplayNPC(baseEntity)) {
+ MobData.MobResult.illegal
+ } else {
+ MobData.MobResult.notYetFound // Maybe a problem in the future
+ }
+
+ else -> null
+ }
+
+ private fun deepCaverns(baseEntity: EntityLivingBase) = when {
+ baseEntity is EntityCreeper && baseEntity.baseMaxHealth.derpy() == 120 ->
+ MobData.MobResult.found(
+ Mob(baseEntity, Mob.Type.BASIC, name = "Sneaky Creeper", levelOrTier = 3),
+ )
+
+ else -> null
+ }
+
+ private fun dwarvenMines(baseEntity: EntityLivingBase) = when {
+ baseEntity is EntityCreeper && baseEntity.baseMaxHealth.derpy() == 1_000_000 ->
+ MobData.MobResult.found(MobFactories.basic(baseEntity, "Ghost"))
+
+ else -> null
+ }
+
+ private fun crystalHollows(
+ baseEntity: EntityLivingBase,
+ armorStand: EntityArmorStand?,
+ ) = when {
+ baseEntity is EntityMagmaCube && armorStand != null &&
+ armorStand.cleanName() == "[Lv100] Bal ???❤" ->
+ MobData.MobResult.found(
+ Mob(baseEntity, Mob.Type.BOSS, armorStand, "Bal", levelOrTier = 100),
+ )
+
+ else -> null
+ }
+
+ private fun hub(
+ baseEntity: EntityLivingBase,
+ armorStand: EntityArmorStand?,
+ nextEntity: EntityLivingBase?,
+ ) = when {
+ baseEntity is EntityOcelot && armorStand?.isDefaultValue() == false &&
+ armorStand.name.startsWith("§8[§7Lv155§8] §cAzrael§r") ->
+ MobUtils.getArmorStand(baseEntity, 1)
+ .makeMobResult { MobFactories.basic(baseEntity, it) }
+
+ baseEntity is EntityOcelot && (nextEntity is EntityOcelot || nextEntity == null) ->
+ MobUtils.getArmorStand(baseEntity, 3)
+ .makeMobResult { MobFactories.basic(baseEntity, it) }
+
+ baseEntity is EntityOtherPlayerMP &&
+ baseEntity.name.let { it == "Minos Champion" || it == "Minos Inquisitor" || it == "Minotaur " } &&
+ armorStand != null ->
+ MobUtils.getArmorStand(baseEntity, 2)
+ .makeMobResult { MobFactories.basic(baseEntity, it, listOf(armorStand)) }
+
+ baseEntity is EntityZombie && armorStand?.isDefaultValue() == true &&
+ MobUtils.getNextEntity(baseEntity, 4)?.name?.startsWith("§e") == true ->
+ petCareHandler(baseEntity)
+
+ baseEntity is EntityZombie && armorStand != null && !armorStand.isDefaultValue() -> null // Impossible Rat
+ baseEntity is EntityZombie -> ratHandler(baseEntity, nextEntity) // Possible Rat
+
+ else -> null
+ }
+
+ private fun garden(baseEntity: EntityLivingBase) = when {
+ baseEntity is EntityOtherPlayerMP && baseEntity.isNPC() ->
+ MobData.MobResult.found(Mob(baseEntity, Mob.Type.DISPLAY_NPC, name = baseEntity.cleanName()))
+
+ else -> null
+ }
+
+ private fun kuudraArena(
+ baseEntity: EntityLivingBase,
+ nextEntity: EntityLivingBase?,
+ ) = when {
+ baseEntity is EntityMagmaCube && nextEntity is EntityMagmaCube -> MobData.MobResult.illegal
+ baseEntity is EntityZombie && nextEntity is EntityZombie -> MobData.MobResult.illegal
+ baseEntity is EntityZombie && nextEntity is EntityGiantZombie -> MobData.MobResult.illegal
+
+ else -> null
+ }
+
+ private fun winterIsland(baseEntity: EntityLivingBase): MobData.MobResult? {
+ val armorStand = MobUtils.getArmorStand(baseEntity, 2)
+ return when {
+ baseEntity is EntityMagmaCube &&
+ MobFilter.jerryMagmaCubePattern.matches(armorStand?.name) ->
+ MobData.MobResult.found(Mob(baseEntity, Mob.Type.BOSS, armorStand, "Jerry Magma Cube"))
else -> null
}
}
+ private const val RAT_SEARCH_START = 1
+ private const val RAT_SEARCH_UP_TO = 11
+
+ private fun ratHandler(baseEntity: EntityZombie, nextEntity: EntityLivingBase?): MobData.MobResult? =
+ generateSequence(RAT_SEARCH_START) { it + 1 }
+ .take(RAT_SEARCH_UP_TO - RAT_SEARCH_START + 1)
+ .map { i -> MobUtils.getArmorStand(baseEntity, i) }
+ .firstOrNull {
+ it != null && it.distanceTo(baseEntity) < 4.0 &&
+ it.inventory?.get(4)?.getSkullTexture() == MobFilter.RAT_SKULL
+ }?.let {
+ MobData.MobResult.found(Mob(baseEntity, mobType = Mob.Type.BASIC, armorStand = it, name = "Rat"))
+ } ?: if (nextEntity is EntityZombie) MobData.MobResult.notYetFound else null
+
private fun petCareHandler(baseEntity: EntityLivingBase): MobData.MobResult {
val extraEntityList = listOf(1, 2, 3, 4).mapNotNull { MobUtils.getArmorStand(baseEntity, it) }
if (extraEntityList.size != 4) return MobData.MobResult.notYetFound
@@ -243,32 +267,9 @@ object IslandExceptions {
armorStand = extraEntityList[1],
name = this.group("name"),
additionalEntities = extraEntityList,
- levelOrTier = this.group("level").toInt()
+ levelOrTier = this.group("level").toInt(),
),
)
} ?: MobData.MobResult.somethingWentWrong
}
-
- private const val ratSearchStart = 1
- private const val ratSearchUpTo = 11
-
- private fun ratHandler(baseEntity: EntityZombie, nextEntity: EntityLivingBase?): MobData.MobResult? =
- generateSequence(ratSearchStart) { it + 1 }.take(ratSearchUpTo - ratSearchStart + 1).map { i ->
- MobUtils.getArmorStand(
- baseEntity, i
- )
- }.firstOrNull {
- it != null && it.distanceTo(baseEntity) < 4.0 && it.inventory?.get(4)
- ?.getSkullTexture() == MobFilter.RAT_SKULL
- }?.let {
- MobData.MobResult.found(
- Mob(
- baseEntity = baseEntity,
- mobType = Mob.Type.BASIC,
- armorStand = it,
- name = "Rat"
- )
- )
- }
- ?: if (nextEntity is EntityZombie) MobData.MobResult.notYetFound else null
}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/mob/Mob.kt b/src/main/java/at/hannibal2/skyhanni/data/mob/Mob.kt
index 1d5827490f54..7e7e9f96cdc8 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/mob/Mob.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/mob/Mob.kt
@@ -115,10 +115,17 @@ class Mob(
private var highlightColor: Color? = null
- /** If no alpha is set or alpha is set to 255 it will set the alpha to 127 */
- fun highlight(color: Color) {
- highlightColor = color.takeIf { it.alpha == 255 }?.addAlpha(127) ?: color
- internalHighlight()
+ /** If [color] has no alpha or alpha is set to 255 it will set the alpha to 127
+ * If [color] is set to null it removes a highlight*/
+ fun highlight(color: Color?) {
+ if (color == highlightColor) return
+ if (color == null) {
+ internalRemoveColor()
+ highlightColor = null
+ } else {
+ highlightColor = color.takeIf { it.alpha == 255 }?.addAlpha(127) ?: color
+ internalHighlight()
+ }
}
private fun internalHighlight() {
@@ -162,8 +169,10 @@ class Mob(
}
private fun makeRelativeBoundingBox() =
- (baseEntity.entityBoundingBox.union(extraEntities.filter { it !is EntityArmorStand }
- .mapNotNull { it.entityBoundingBox }))?.offset(-baseEntity.posX, -baseEntity.posY, -baseEntity.posZ)
+ (baseEntity.entityBoundingBox.union(
+ extraEntities.filter { it !is EntityArmorStand }
+ .mapNotNull { it.entityBoundingBox },
+ ))?.offset(-baseEntity.posX, -baseEntity.posY, -baseEntity.posZ)
fun fullEntityList() =
baseEntity.toSingletonListOrEmpty() +
diff --git a/src/main/java/at/hannibal2/skyhanni/data/mob/MobFilter.kt b/src/main/java/at/hannibal2/skyhanni/data/mob/MobFilter.kt
index da8699bca2fb..a9f8a14533ac 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/mob/MobFilter.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/mob/MobFilter.kt
@@ -103,6 +103,10 @@ object MobFilter {
"pattern.summon.owner",
".*Spawned by: (?.*).*",
)
+ val heavyPearlPattern by repoGroup.pattern(
+ "pattern.heavypearl.collect",
+ "§.§lCOLLECT!",
+ )
/**
* REGEX-TEST: §8[§7Lv1§8] §5Horse
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt
index ab5d1ca2d851..21440197d8b0 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/Graph.kt
@@ -7,12 +7,12 @@ import at.hannibal2.skyhanni.utils.json.fromJson
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.annotations.Expose
+import com.google.gson.stream.JsonToken
import java.util.PriorityQueue
@JvmInline
value class Graph(
- @Expose
- val graph: List,
+ @Expose val graph: List,
) : List {
override val size
get() = graph.size
@@ -42,17 +42,30 @@ value class Graph(
out.beginObject()
value.forEach {
out.name(it.id.toString()).beginObject()
+
out.name("Position").value(with(it.position) { "$x:$y:$z" })
- if (it.name != null) {
- out.name("Name").value(it.name)
+
+ it.name?.let {
+ out.name("Name").value(it)
+ }
+
+ it.tagNames?.takeIf { it.isNotEmpty() }?.let {
+ out.name("Tags")
+ out.beginArray()
+ for (tagName in it) {
+ out.value(tagName)
+ }
+ out.endArray()
}
+
out.name("Neighbours")
out.beginObject()
- it.neighbours.forEach { (node, weight) ->
+ for ((node, weight) in it.neighbours) {
val id = node.id.toString()
out.name(id).value(weight.round(2))
}
out.endObject()
+
out.endObject()
}
out.endObject()
@@ -66,8 +79,13 @@ value class Graph(
reader.beginObject()
var position: LorenzVec? = null
var name: String? = null
+ var tags: List? = null
var neighbors = mutableListOf>()
while (reader.hasNext()) {
+ if (reader.peek() != JsonToken.NAME) {
+ reader.skipValue()
+ continue
+ }
when (reader.nextName()) {
"Position" -> {
position = reader.nextString().split(":").let { parts ->
@@ -89,9 +107,19 @@ value class Graph(
name = reader.nextString()
}
+ "Tags" -> {
+ tags = mutableListOf()
+ reader.beginArray()
+ while (reader.hasNext()) {
+ val tagName = reader.nextString()
+ tags.add(tagName)
+ }
+ reader.endArray()
+ }
+
}
}
- val node = GraphNode(id, position!!, name)
+ val node = GraphNode(id, position!!, name, tags)
list.add(node)
neighbourMap[node] = neighbors
reader.endObject()
@@ -111,7 +139,8 @@ value class Graph(
}
}
-class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null) {
+// The node object that gets parsed from/to json
+class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null, val tagNames: List? = null) {
/** Keys are the neighbours and value the edge weight (e.g. Distance) */
lateinit var neighbours: Map
@@ -132,8 +161,7 @@ class GraphNode(val id: Int, val position: LorenzVec, val name: String? = null)
}
}
-fun Graph.findShortestPathAsGraph(start: GraphNode, end: GraphNode): Graph =
- this.findShortestPathAsGraphWithDistance(start, end).first
+fun Graph.findShortestPathAsGraph(start: GraphNode, end: GraphNode): Graph = this.findShortestPathAsGraphWithDistance(start, end).first
fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode): Pair {
val distances = mutableMapOf()
@@ -174,11 +202,9 @@ fun Graph.findShortestPathAsGraphWithDistance(start: GraphNode, end: GraphNode):
) to distances[end]!!
}
-fun Graph.findShortestPath(start: GraphNode, end: GraphNode): List =
- this.findShortestPathAsGraph(start, end).toPositionsList()
+fun Graph.findShortestPath(start: GraphNode, end: GraphNode): List = this.findShortestPathAsGraph(start, end).toPositionsList()
-fun Graph.findShortestDistance(start: GraphNode, end: GraphNode): Double =
- this.findShortestPathAsGraphWithDistance(start, end).second
+fun Graph.findShortestDistance(start: GraphNode, end: GraphNode): Double = this.findShortestPathAsGraphWithDistance(start, end).second
fun Graph.toPositionsList() = this.map { it.position }
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt
new file mode 100644
index 000000000000..a7349801324a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/GraphNodeTag.kt
@@ -0,0 +1,46 @@
+package at.hannibal2.skyhanni.data.model
+
+import at.hannibal2.skyhanni.utils.LorenzColor
+
+enum class GraphNodeTag(val internalName: String?, val color: LorenzColor, val cleanName: String, val description: String) {
+ DEV("dev", LorenzColor.WHITE, "Dev", "Intentionally marked as dev."), // E.g. Spawn points, todos, etc
+
+ // Everywhere
+ NPC("npc", LorenzColor.YELLOW, "NPC", "A NPC entity."), // also take from neu repo
+ AREA("area", LorenzColor.DARK_GREEN, "Area", "A big SkyBlock area."),
+ SMALL_AREA("small_area", LorenzColor.GREEN, "Small Area", "A small SkyBlock area, e.g. a house."),
+ POI("poi", LorenzColor.WHITE, "PoI", "Point of interest."),
+ LAUNCH_PAD("launch", LorenzColor.WHITE, "Launch Pad", "Slime blocks sending you to another server."),
+
+ // on multiple islands
+ ROMEO("romeo", LorenzColor.WHITE, "Romeo & Juliette Quest", "Blocks related to the Romeo and Juliette/Ring of Love quest line."),
+ RACE("race", LorenzColor.WHITE, "Race Start/Stop", "A race start or stop point."),
+ SLAYER("slayer", LorenzColor.WHITE, "Slayer", "A Slayer area"),
+ // hoppity
+
+ // Hub
+ HUB_12_STARTER("starter_npc", LorenzColor.WHITE, "Starter NPC", "One of the 12 starter NPC's you need to talk to."),
+ // diana
+
+ // Farming Islands: Pelts
+ FARMING_CROP("farming_crop", LorenzColor.WHITE, "Farming Crop", "A spot where you can break crops on farming islands."),
+
+ // Rift
+ RIFT_ENIGMA("rift_enigma", LorenzColor.DARK_PURPLE, "Enigma Soul", "Enigma Souls in the rift."),
+ RIFT_EYE("rift_eye", LorenzColor.DARK_RED, "Eye", "An Eye in the rift to teleport to."),
+
+ // Spider's Den
+ SPIDER_RELIC("SPIDER_RELIC", LorenzColor.DARK_PURPLE, "Relic", "An relic in the Spider's Den."),
+
+ // Dwarven Mines
+ MINES_EMISSARY("mines_emissary", LorenzColor.GOLD, "Emissary", "A Emissary from the king."),
+ // commission areas
+
+ ;
+
+ val displayName: String = color.getChatColor() + cleanName
+
+ companion object {
+ fun byId(internalName: String?): GraphNodeTag? = values().firstOrNull { it.internalName == internalName }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt b/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt
index 9d922f93d60a..523121b3763d 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/TabWidget.kt
@@ -55,7 +55,7 @@ enum class TabWidget(
),
PROFILE(
// language=RegExp
- "(?:§.)*Profile: (?:§.)*(?\\S+).*",
+ "(?:§.)+Profile: §r§a(?[\\w\\s]+[^ §]).*",
),
SB_LEVEL(
// language=RegExp
@@ -307,6 +307,10 @@ enum class TabWidget(
// language=RegExp
"Scrap: (?:§.)*(?\\d)(?:§.)*/(?:§.)*\\d",
),
+ EVENT_TRACKERS(
+ // language=RegExp
+ "§e§lEvent Trackers:",
+ )
;
diff --git a/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt b/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt
index f1a5001ad67c..0646b884cdec 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/model/TextInput.kt
@@ -2,11 +2,14 @@ package at.hannibal2.skyhanni.data.model
import at.hannibal2.skyhanni.utils.KeyboardManager
import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyClicked
+import at.hannibal2.skyhanni.utils.KeyboardManager.isKeyHeld
+import at.hannibal2.skyhanni.utils.LorenzColor
import at.hannibal2.skyhanni.utils.OSUtils
import at.hannibal2.skyhanni.utils.StringUtils.insert
import kotlinx.coroutines.runBlocking
import net.minecraft.client.settings.KeyBinding
import org.lwjgl.input.Keyboard
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
class TextInput {
@@ -14,23 +17,42 @@ class TextInput {
var textBox: String = ""
private var carriage: Int? = null
- fun editText() = textBox.let {
+ fun editText(textColor: LorenzColor = LorenzColor.WHITE, carriageColor: LorenzColor = LorenzColor.GREEN) = textBox.let {
with(carriage) {
if (this == null) it
+ else it.insert(this, "${carriageColor.getChatColor()}|${textColor.getChatColor()}")
+ }
+ }.replace("(? Unit>()
+
+ fun registerToEvent(key: Int, event: (TextInput) -> Unit) {
+ updateEvents[key] = event
+ }
+
+ fun removeFromEvent(key: Int) {
+ updateEvents.remove(key)
+ }
+
companion object {
private var activeInstance: TextInput? = null
@@ -50,6 +72,17 @@ class TextInput {
}
}
+ fun onGuiInput(ci: CallbackInfo) {
+ if (activeInstance != null) {
+ if (Keyboard.KEY_ESCAPE.isKeyHeld()) {
+ disable()
+ } else {
+ ci.cancel()
+ }
+ return
+ }
+ }
+
private var timeSinceKeyEvent = 0L
private var carriage
@@ -64,6 +97,13 @@ class TextInput {
activeInstance?.textBox = value
}
+ private fun updated() {
+ with(activeInstance) {
+ if (this == null) return
+ this.updateEvents.forEach { (_, it) -> it(this) }
+ }
+ }
+
private fun handleTextInput() {
if (KeyboardManager.isCopyingKeysDown()) {
OSUtils.copyToClipboard(textBox)
@@ -72,6 +112,7 @@ class TextInput {
if (KeyboardManager.isPastingKeysDown()) {
runBlocking {
textBox = OSUtils.readFromClipboard() ?: return@runBlocking
+ updated()
}
return
}
@@ -95,6 +136,7 @@ class TextInput {
} else {
textBox.dropLast(1)
}
+ updated()
return
}
@@ -121,6 +163,7 @@ class TextInput {
textBox + char
}
}
+ updated()
}
private fun moveCarriageRight(carriage: Int) = carriage + 1
diff --git a/src/main/java/at/hannibal2/skyhanni/data/repo/RepoManager.kt b/src/main/java/at/hannibal2/skyhanni/data/repo/RepoManager.kt
index 79d329f2207a..551f4469947a 100644
--- a/src/main/java/at/hannibal2/skyhanni/data/repo/RepoManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/data/repo/RepoManager.kt
@@ -7,12 +7,12 @@ import at.hannibal2.skyhanni.events.NeuRepositoryReloadEvent
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.chat.Text
import at.hannibal2.skyhanni.utils.chat.Text.asComponent
import at.hannibal2.skyhanni.utils.chat.Text.send
import com.google.gson.JsonObject
-import net.minecraft.client.Minecraft
import net.minecraft.util.IChatComponent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import org.apache.commons.io.FileUtils
@@ -169,7 +169,7 @@ class RepoManager(private val configLocation: File) {
val comp = CompletableFuture()
if (!atomicShouldManuallyReload.get()) return comp
ErrorManager.resetCache()
- Minecraft.getMinecraft().addScheduledTask {
+ DelayedRun.onThread.execute {
error = false
successfulConstants.clear()
unsuccessfulConstants.clear()
diff --git a/src/main/java/at/hannibal2/skyhanni/events/DungeonClickedBlockEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/DungeonClickedBlockEvent.kt
new file mode 100644
index 000000000000..9a7f5119aa64
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/DungeonClickedBlockEvent.kt
@@ -0,0 +1,7 @@
+package at.hannibal2.skyhanni.events
+
+import at.hannibal2.skyhanni.api.event.SkyHanniEvent
+import at.hannibal2.skyhanni.data.ClickedBlockType
+import at.hannibal2.skyhanni.utils.LorenzVec
+
+class DungeonBlockClickEvent(val position: LorenzVec, val blockType: ClickedBlockType) : SkyHanniEvent()
diff --git a/src/main/java/at/hannibal2/skyhanni/events/MobEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/MobEvent.kt
index 07990ff222c5..10172cb5fcd8 100644
--- a/src/main/java/at/hannibal2/skyhanni/events/MobEvent.kt
+++ b/src/main/java/at/hannibal2/skyhanni/events/MobEvent.kt
@@ -21,6 +21,8 @@ open class MobEvent(val mob: Mob) : LorenzEvent() {
class Projectile(mob: Mob) : DeSpawn(mob)
}
+ // TODO replace with "isFirstTime" parameter in the Spawn event. Also create an actual "player sees the mob for the first time" event
+ @Deprecated("Old. Will get replaced soon.")
open class FirstSeen(mob: Mob) : MobEvent(mob) {
class SkyblockMob(mob: Mob) : FirstSeen(mob)
class Summon(mob: Mob) : FirstSeen(mob)
diff --git a/src/main/java/at/hannibal2/skyhanni/events/entity/EntityHealthDisplayEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/entity/EntityHealthDisplayEvent.kt
new file mode 100644
index 000000000000..5657dc2c83da
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/entity/EntityHealthDisplayEvent.kt
@@ -0,0 +1,5 @@
+package at.hannibal2.skyhanni.events.entity
+
+import at.hannibal2.skyhanni.events.LorenzEvent
+
+class EntityHealthDisplayEvent(var text: String) : LorenzEvent()
diff --git a/src/main/java/at/hannibal2/skyhanni/events/hoppity/RabbitFoundEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/hoppity/RabbitFoundEvent.kt
new file mode 100644
index 000000000000..865f5a69047d
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/hoppity/RabbitFoundEvent.kt
@@ -0,0 +1,14 @@
+package at.hannibal2.skyhanni.events.hoppity
+
+import at.hannibal2.skyhanni.api.event.SkyHanniEvent
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType
+
+class RabbitFoundEvent(
+ val eggType: HoppityEggType,
+ val duplicate: Boolean,
+ val rabbitName: String,
+ val chocGained: Long = 0,
+) : SkyHanniEvent() {
+ override fun toString(): String =
+ "§fType§7: ${eggType.coloredName}\n§fDuplicate§7: §b$duplicate\n§fRabbit§7: $rabbitName\n§fChoc Gained§7: §6$chocGained"
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/events/mining/PowderGainEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/mining/PowderGainEvent.kt
new file mode 100644
index 000000000000..15bf3ee58119
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/events/mining/PowderGainEvent.kt
@@ -0,0 +1,6 @@
+package at.hannibal2.skyhanni.events.mining
+
+import at.hannibal2.skyhanni.api.HotmAPI
+import at.hannibal2.skyhanni.api.event.SkyHanniEvent
+
+class PowderGainEvent(val powder: HotmAPI.PowderType, val amount: Long) : SkyHanniEvent()
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/ArachneChatMessageHider.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/ArachneChatMessageHider.kt
index 5bf5b34489a1..846458456ad1 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/chat/ArachneChatMessageHider.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/ArachneChatMessageHider.kt
@@ -17,11 +17,11 @@ object ArachneChatMessageHider {
private var hideArachneDeadMessage = false
private val patternGroup = RepoPattern.group("chat.arachne")
- private val arachneCallingPattern by patternGroup.pattern(
+ val arachneCallingPattern by patternGroup.pattern(
"calling",
"§4☄ §r.* §r§eplaced an §r§9Arachne's Calling§r§e!.*"
)
- private val arachneCrystalPattern by patternGroup.pattern(
+ val arachneCrystalPattern by patternGroup.pattern(
"crystal",
"§4☄ §r.* §r§eplaced an Arachne Crystal! Something is awakening!"
)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt
index fe166e9aedc0..c7d80319048a 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/ChatFilter.kt
@@ -7,6 +7,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.features.chat.PowderMiningChatFilter.genericMiningRewardMessage
import at.hannibal2.skyhanni.features.dungeon.DungeonAPI
import at.hannibal2.skyhanni.features.garden.GardenAPI
+import at.hannibal2.skyhanni.features.garden.pests.PestFinder
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull
@@ -425,6 +426,7 @@ object ChatFilter {
"§4Cancelled parkour! You cannot use item abilities.",
"§4Cancelled parkour!",
)
+
/**
* REGEX-TEST: §r§aWarped from the tp_pad_one §r§ato the tp_pad_two§r§a!
*/
@@ -514,8 +516,8 @@ object ChatFilter {
config.guildExp && message.isPresent("guild_exp") -> "guild_exp"
config.killCombo && message.isPresent("kill_combo") -> "kill_combo"
config.profileJoin && message.isPresent("profile_join") -> "profile_join"
- config.parkour && message.isPresent("parkour") -> "parkour"
- config.teleport_pads && message.isPresent("teleport_pads") -> "teleport_pads"
+ config.hideParkour && message.isPresent("parkour") -> "parkour"
+ config.hideTeleportPads && message.isPresent("teleport_pads") -> "teleport_pads"
config.hideAlphaAchievements && HypixelData.hypixelAlpha && message.isPresent("achievement_get") -> "achievement_get"
@@ -535,6 +537,7 @@ object ChatFilter {
dungeonConfig.soloClass && DungeonAPI.inDungeon() && message.isPresent("solo_class") -> "solo_class"
dungeonConfig.soloStats && DungeonAPI.inDungeon() && message.isPresent("solo_stats") -> "solo_stats"
dungeonConfig.fairy && DungeonAPI.inDungeon() && message.isPresent("fairy") -> "fairy"
+ config.gardenNoPest && GardenAPI.inGarden() && PestFinder.noPestsChatPattern.matches(message) -> "garden_pest"
else -> null
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/ColorFormattingHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/ColorFormattingHelper.kt
new file mode 100644
index 000000000000..9c7c0380614b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/ColorFormattingHelper.kt
@@ -0,0 +1,60 @@
+package at.hannibal2.skyhanni.features.chat
+
+import at.hannibal2.skyhanni.utils.ChatUtils
+import net.minecraft.util.ChatComponentText
+
+object ColorFormattingHelper {
+ fun printColorCodeList() {
+ ChatUtils.chat(
+ ChatComponentText(
+ "§c=================== General Colors ===================\n" +
+ "§f&0 = §0Black §f&1 = §1Dark Blue\n" +
+ "§f&2 = §2Dark Green §f&3 = §3Dark Aqua\n" +
+ "§f&4 = §4Dark Red §f&5 = §5Dark Purple\n" +
+ "§f&6 = §6Gold §f&7 = §7Gray\n" +
+ "§f&8 = §8Dark Gray §f&9 = §9Blue\n" +
+ "§f&a = §aGreen §f&b = §bAqua\n" +
+ "§f&c = §cRed §f&d = §dLight Purple\n" +
+ "§f&e = §eYellow §f&f = §fWhite\n" +
+ "§f&Z = §zChroma §r(needs to enable chroma setting)\n" +
+ "§c================= Formatting Codes ==================\n" +
+ "§f&k = Obfuscated (like this: §khellspawn§r)\n" +
+ "§f&l = §lBold §r&m = §mStrikethrough \n" +
+ "§f&o = §oItalic §r&n = §nUnderline\n" +
+ "§f&r = Reset\n"+
+ "§c==================================================="
+ )
+ )
+ ChatUtils.clickableChat(
+ "§eClick to view extra info about colors and formatting.",
+ onClick = { printColorCodesExtra() },
+ "§eClick to see more!",
+ prefix = false,
+ )
+ }
+
+ private fun printColorCodesExtra() {
+ ChatUtils.chat("§c================= Formatting Extra ==================", false)
+ ChatUtils.clickableLinkChat(
+ "§#§6§a§e§e§4§8§/[Click here to view codes on minecraft.wiki]",
+ "https://minecraft.wiki/w/Formatting_codes#Color_codes",
+ "§eOpen §cminecraft.wiki§e!",
+ false,
+ false,
+ )
+ ChatUtils.chat(
+ "§eYou can also uses SkyHanni's system for any colors. " +
+ "This is different from chroma. " +
+ "Simply type §6&f&f&9&a&2&e&/ §efor color §#§f§f§9§a§2§e§/#ff9a2e§e " +
+ "(adds §6& §ebefore every characters including §6#§e, ends with '§6&/§e').",
+ false,
+ )
+ ChatUtils.clickableLinkChat(
+ "§z[Click here to open color picker color-hex.com]",
+ url = "https://www.color-hex.com",
+ "§eOpen §ccolor-hex.com§e!",
+ prefix = false,
+ )
+ ChatUtils.chat("§c===================================================", false)
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/CompactExperimentRewards.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/CompactExperimentRewards.kt
new file mode 100644
index 000000000000..286166ee7c0b
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/CompactExperimentRewards.kt
@@ -0,0 +1,104 @@
+package at.hannibal2.skyhanni.features.chat
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.InventoryCloseEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
+
+@SkyHanniModule
+object CompactExperimentRewards {
+
+ private val config get() = SkyHanniMod.feature.chat
+
+ private var gainedRewards = mutableListOf()
+ private var lastTimeTableOpened = SimpleTimeMark.farPast()
+ private var currentMessage = ""
+
+ private val patternGroup = RepoPattern.group("chat.experiments.compact")
+
+ /**
+ * REGEX-TEST: Superpairs (Metaphysical)
+ * REGEX-TEST: Superpairs Rewards
+ */
+ private val experimentInventoriesPattern by patternGroup.pattern(
+ "inventories",
+ "(?:Superpairs|Chronomatron|Ultrasequencer) (?:\\(.+\\)|➜ Stakes|Rewards)|Experimentation Table",
+ )
+
+ /**
+ * REGEX-TEST: §eYou claimed the §r§dUltrasequencer §r§erewards!
+ * REGEX-TEST: §eYou claimed the §r§cUltrasequencer §r§erewards!
+ */
+ private val claimMessagePattern by patternGroup.pattern(
+ "message",
+ "(?§eYou claimed the §r§.\\S+ §r§erewards!)",
+ )
+
+ /**
+ * REGEX-TEST: §8 +§r§3600k Enchanting Exp
+ * REGEX-TEST: §r§8+§r§3132k Enchanting Exp
+ * REGEX-TEST: §r§8+§r§aThunderlord V
+ * REGEX-TEST: §r§8+§r§3143k Enchanting Exp
+ * REGEX-TEST: §r§8+§r§aGrand Experience Bottle
+ * REGEX-TEST: §r§8+§r§aCaster V
+ */
+ private val experimentsDropPattern by patternGroup.pattern(
+ "drop",
+ "^(?:§8 \\+| §r§8\\+)(?.*)\$",
+ )
+
+ @SubscribeEvent
+ fun onInventoryClose(event: InventoryCloseEvent) {
+ if (isEnabled() && experimentInventoriesPattern.matches(InventoryUtils.openInventoryName())) {
+ lastTimeTableOpened = SimpleTimeMark.now()
+ }
+ }
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled() || lastTimeTableOpened.passedSince() >= 3.seconds || event.blockedReason != "") return
+
+ val message = event.message
+ claimMessagePattern.matchMatcher(message) {
+ currentMessage = group("message")
+ event.blockedReason = "COMPACT_REWARDS"
+ return
+ }
+ experimentsDropPattern.matchMatcher(message) {
+ val reward = group("reward")
+
+ gainedRewards.add(reward)
+ // TODO check price and only block when below x coins (user option)
+ event.blockedReason = "COMPACT_REWARDS"
+
+ DelayedRun.runDelayed(100.milliseconds) {
+ sendMessage(reward)
+ }
+ }
+ }
+
+ private fun sendMessage(reward: String?) {
+ if (gainedRewards.last() != reward || currentMessage == "") return
+
+ val expList = mutableListOf().apply {
+ gainedRewards.forEach { add("§8+$it") }
+ }
+
+ ChatUtils.hoverableChat(currentMessage, expList, null, false)
+ gainedRewards.clear()
+ currentMessage = ""
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.compactExperimentationTable
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/translation/TranslatableLanguage.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/TranslatableLanguage.kt
new file mode 100644
index 000000000000..13a19a1dcbc8
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/TranslatableLanguage.kt
@@ -0,0 +1,45 @@
+package at.hannibal2.skyhanni.features.chat.translation
+
+enum class TranslatableLanguage(private val englishName: String, private val nativeName: String, val languageCode: String) {
+
+ // 1. First Language - The primary language of the application.
+ ENGLISH("English", "", "en"),
+
+ // 2. Well Supported - Major languages commonly used in Europe and North America.
+ SPANISH("Spanish", "Español", "es"), // Major language in Spain and Latin America
+ GERMAN("German", "Deutsch", "de"), // Important language in Germany, Austria, and Switzerland
+ FRENCH("French", "Français", "fr"), // Significant language in France, Canada, and parts of Africa
+ DUTCH("Dutch", "Nederlands", "nl"), // Spoken in the Netherlands and Belgium
+ RUSSIAN("Russian", "Русский", "ru"), // Major language in Russia and other parts of Eastern Europe and Central Asia
+ POLISH("Polish", "Polski", "pl"), // Spoken primarily in Poland
+ ITALIAN("Italian", "Italiano", "it"), // Important language in Italy and parts of Switzerland
+ UKRAINIAN("Ukrainian", "Українська", "uk"), // Spoken in Ukraine
+ PORTUGUESE("Portuguese", "Português", "pt"), // Spoken in Portugal and Brazil
+ TURKISH("Turkish", "Türkçe", "tr"), // Significant in Turkey and Central Asia
+ SWEDISH("Swedish", "Svenska", "sv"), // Relevant in Northern Europe
+
+ // 3. Global Languages - Widely spoken languages with significant global presence.
+ CHINESE("Chinese", "中文", "zh"), // Major language in China and other parts of East Asia
+ ARABIC("Arabic", "العربية", "ar"), // Significant language in the Middle East and North Africa
+ JAPANESE("Japanese", "日本語", "ja"), // Major language in Japan
+ HINDI("Hindi", "हिन्दी", "hi"), // Major language in India
+ BENGALI("Bengali", "বাংলা", "bn"), // Widely spoken in India and Bangladesh
+ KOREAN("Korean", "한국어", "ko"), // Important for East Asia
+ VIETNAMESE("Vietnamese", "Tiếng Việt", "vi"), // Major language in Vietnam
+ INDONESIAN("Indonesian", "Bahasa Indonesia", "id"), // Key language in Southeast Asia
+ THAI("Thai", "ภาษาไทย", "th"), // Important in Thailand
+
+ // 4. Other Supported Languages
+ PERSIAN("Persian", "فارسی", "fa"), // Spoken in Iran and other parts of the Middle East
+ TAGALOG("Tagalog", "Tagalog", "tl"), // Major language in the Philippines
+ PUNJABI("Punjabi", "ਪੰਜਾਬੀ", "pa"), // Significant in India and Pakistan
+
+ // 5. need better name
+ UNKNOWN("Unknown Language", "", ""),
+ ;
+
+ // Limit to 20 characters so that the text is not too small in the config
+ private val displayName: String = if (nativeName.isBlank()) englishName else "$englishName/$nativeName".take(20)
+
+ override fun toString(): String = displayName
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt
similarity index 71%
rename from src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt
rename to src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt
index 2d3f3aa85bdc..389b57fb0fc7 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/chat/Translator.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/chat/translation/Translator.kt
@@ -1,13 +1,17 @@
-package at.hannibal2.skyhanni.features.chat
+package at.hannibal2.skyhanni.features.chat.translation
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.SkyHanniMod.Companion.coroutineScope
+import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.APIUtil
import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle
import at.hannibal2.skyhanni.utils.ConditionalUtils.transformIf
import at.hannibal2.skyhanni.utils.OSUtils
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils.getPlayerNameFromChatMessage
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import com.google.gson.JsonArray
@@ -20,6 +24,7 @@ import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.net.URLDecoder
import java.net.URLEncoder
+import kotlin.time.Duration.Companion.milliseconds
// TODO split into two classes: TranslatorCommand and GoogleTranslator. only communicates via getTranslationFromEnglish and getTranslationToEnglish
@SkyHanniModule
@@ -44,6 +49,41 @@ object Translator {
editedComponent.setChatStyle(clickStyle)
}
+ @SubscribeEvent
+ fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
+ event.move(55, "chat.translator", "chat.translator.translateOnClick")
+ }
+
+ var lastUserChange = SimpleTimeMark.farPast()
+
+ @SubscribeEvent
+ fun onConfigReload(event: ConfigLoadEvent) {
+ config.languageCode.onToggle {
+ if (lastUserChange.passedSince() < 50.milliseconds) return@onToggle
+ lastUserChange = SimpleTimeMark.now()
+
+ val text = config.languageCode.get()
+ if (text.isEmpty()) {
+ config.languageName.set(TranslatableLanguage.ENGLISH)
+ } else {
+ for (language in TranslatableLanguage.values()) {
+ if (language.languageCode.equals(text, ignoreCase = true)) {
+ config.languageName.set(language)
+ return@onToggle
+ }
+ }
+ config.languageName.set(TranslatableLanguage.UNKNOWN)
+ }
+ }
+
+ config.languageName.onToggle {
+ if (lastUserChange.passedSince() < 50.milliseconds) return@onToggle
+ lastUserChange = SimpleTimeMark.now()
+
+ config.languageCode.set(config.languageName.get().languageCode)
+ }
+ }
+
private fun createClickStyle(message: String, style: ChatStyle): ChatStyle {
val text = messageContentRegex.find(message)!!.groupValues[1].removeColor()
style.setChatClickEvent(ClickEvent(ClickEvent.Action.RUN_COMMAND, "/shtranslate $text"))
@@ -51,7 +91,7 @@ object Translator {
return style
}
- private val config get() = SkyHanniMod.feature.chat
+ private val config get() = SkyHanniMod.feature.chat.translator
/*
* Simplified version of the JSON response:
@@ -130,9 +170,22 @@ object Translator {
val message = args.joinToString(" ").removeColor()
coroutineScope.launch {
- val translation = getTranslationToEnglish(message)
- if (translation == "Unable to translate!") ChatUtils.userError("Unable to translate message :( (is it in English?)")
- else ChatUtils.chat("Found translation: §f$translation")
+ var lang = config.languageCode.get()
+ val translation = if (lang.isEmpty()) {
+ getTranslationToEnglish(message)
+ } else {
+ getTranslationFromEnglish(message, lang)
+ }
+ if (message == translation) {
+ ChatUtils.userError("Translation is the same as the original message!")
+ return@launch
+ }
+
+ if (translation == "Unable to translate!") {
+ ChatUtils.userError("Unable to translate message :( (is it in English?)")
+ return@launch
+ }
+ ChatUtils.chat("Found translation: §f$translation")
}
}
@@ -151,5 +204,5 @@ object Translator {
}
}
- fun isEnabled() = config.translator
+ fun isEnabled() = config.translateOnClick
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/endernodetracker/EnderNodeTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/endernodetracker/EnderNodeTracker.kt
index 12bd69658880..51deea2b865d 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/combat/endernodetracker/EnderNodeTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/combat/endernodetracker/EnderNodeTracker.kt
@@ -12,8 +12,8 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.OwnInventoryItemUpdateEvent
import at.hannibal2.skyhanni.events.SackChangeEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.ConditionalUtils.afterChange
import at.hannibal2.skyhanni.utils.ConfigUtils
import at.hannibal2.skyhanni.utils.InventoryUtils
@@ -26,6 +26,7 @@ import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
import at.hannibal2.skyhanni.utils.NEUItems.getPriceOrNull
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat
+import at.hannibal2.skyhanni.utils.renderables.Searchable
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
import at.hannibal2.skyhanni.utils.tracker.TrackerData
import com.google.gson.annotations.Expose
@@ -211,36 +212,35 @@ object EnderNodeTracker {
else -> null
}
- private fun drawDisplay(data: Data) = buildList> {
+ private fun drawDisplay(data: Data) = buildList {
val lootProfit = getLootProfit(data)
- addAsSingletonList("§5§lEnder Node Tracker")
- addAsSingletonList("§d${data.totalNodesMined.addSeparators()} Ender Nodes mined")
- addAsSingletonList("§6${lootProfit.values.sum().shortFormat()} Coins made")
- addAsSingletonList(" ")
- addAsSingletonList("§b${data.totalEndermiteNests.addSeparators()} §cEndermite Nest")
+ addSearchString("§5§lEnder Node Tracker")
+ addSearchString("§d${data.totalNodesMined.addSeparators()} Ender Nodes mined")
+ addSearchString("§6${lootProfit.values.sum().shortFormat()} Coins made")
+ addSearchString(" ")
+ addSearchString("§b${data.totalEndermiteNests.addSeparators()} §cEndermite Nest", "Endermite Nest")
for (item in EnderNode.entries.subList(0, 11)) {
val count = (data.lootCount[item] ?: 0).addSeparators()
val profit = (lootProfit[item] ?: 0.0).shortFormat()
- addAsSingletonList("§b$count ${item.displayName} §7(§6$profit§7)")
+ addSearchString("§b$count ${item.displayName} §7(§6$profit§7)", item.displayName)
}
- addAsSingletonList(" ")
+ addSearchString(" ")
val totalEnderArmor = calculateEnderArmor(data)
- addAsSingletonList(
- "§b${totalEnderArmor.addSeparators()} §5Ender Armor " +
- "§7(§6${(totalEnderArmor * 10_000).shortFormat()}§7)"
+ addSearchString(
+ "§b${totalEnderArmor.addSeparators()} §5Ender Armor " + "§7(§6${(totalEnderArmor * 10_000).shortFormat()}§7)"
)
for (item in EnderNode.entries.subList(11, 16)) {
val count = (data.lootCount[item] ?: 0).addSeparators()
val profit = (lootProfit[item] ?: 0.0).shortFormat()
- addAsSingletonList("§b$count ${item.displayName} §7(§6$profit§7)")
+ addSearchString("§b$count ${item.displayName} §7(§6$profit§7)")
}
// enderman pet rarities
val (c, u, r, e, l) = EnderNode.entries.subList(16, 21).map { (data.lootCount[it] ?: 0).addSeparators() }
val profit = EnderNode.entries.subList(16, 21).sumOf { lootProfit[it] ?: 0.0 }.shortFormat()
- addAsSingletonList("§f$c§7-§a$u§7-§9$r§7-§5$e§7-§6$l §fEnderman Pet §7(§6$profit§7)")
+ addSearchString("§f$c§7-§a$u§7-§9$r§7-§5$e§7-§6$l §fEnderman Pet §7(§6$profit§7)")
}
private fun calculateEnderArmor(storage: Data) =
@@ -248,10 +248,10 @@ object EnderNodeTracker {
.map { it.value }
.sum()
- private fun formatDisplay(map: List>): List> {
+ private fun formatDisplay(map: List): List {
if (!ProfileStorageData.loaded) return emptyList()
- val newList = mutableListOf>()
+ val newList = mutableListOf()
for (index in config.textFormat.get()) {
// TODO, change functionality to use enum rather than ordinals
newList.add(map[index.ordinal])
diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneKillTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneKillTimer.kt
new file mode 100644
index 000000000000..d4980ec506f4
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/ArachneKillTimer.kt
@@ -0,0 +1,88 @@
+package at.hannibal2.skyhanni.features.combat.mobs
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.features.chat.ArachneChatMessageHider
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration
+
+@SkyHanniModule
+object ArachneKillTimer {
+
+ private val config get() = SkyHanniMod.feature.combat.mobs
+
+ private val patternGroup = RepoPattern.group("chat.arachne")
+
+ /**
+ * REGEX-TEST: §c[BOSS] Arachne§r§f: A befitting welcome!
+ */
+ private val arachneCallingSpawnedPattern by patternGroup.pattern(
+ "calling.spawned",
+ "§c\\[BOSS] Arachne§r§f: A befitting welcome!"
+ )
+ /**
+ * REGEX-TEST: §c[BOSS] Arachne§r§f: With your sacrifice.
+ */
+ private val arachneCrystalSpawnedPattern by patternGroup.pattern(
+ "crystal.spawned",
+ "§c\\[BOSS] Arachne§r§f: With your sacrifice."
+ )
+ /**
+ * REGEX-TEST: §f §r§6§lARACHNE DOWN!
+ */
+ private val arachneDeathPattern by patternGroup.pattern(
+ "dead",
+ "§f.*§r§6§lARACHNE DOWN!"
+ )
+ /**
+ * REGEX-TEST: §f §r§eYour Damage: §r§a1,155,000 §r§7(Position #1)
+ */
+ private val arachneDamagePattern by patternGroup.pattern(
+ "damage",
+ "§f +§r§eYour Damage: §r§a[0-9,]+ §r§7\\(Position #[0-9,]+\\)"
+ )
+
+ private var arachneSpawnedTime = SimpleTimeMark.farPast()
+ private var arachneKillTime = Duration.ZERO
+
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!isEnabled()) return
+ if (arachneCallingSpawnedPattern.matches(event.message) || arachneCrystalSpawnedPattern.matches(event.message)) {
+ arachneSpawnedTime = SimpleTimeMark.now()
+ }
+
+ if (arachneDeathPattern.matches(event.message) && arachneSpawnedTime != SimpleTimeMark.farPast()) {
+ arachneKillTime = arachneSpawnedTime.passedSince()
+ }
+
+ if (ArachneChatMessageHider.arachneCallingPattern.matches(event.message) ||
+ ArachneChatMessageHider.arachneCrystalPattern.matches(event.message)
+ ) {
+ arachneSpawnedTime = SimpleTimeMark.farPast()
+ }
+
+ if (arachneKillTime.isPositive() && arachneDamagePattern.matches(event.message)) {
+ val format = arachneKillTime.format(showMilliSeconds = true)
+ ChatUtils.chat(" §eArachne took §b$format§e seconds to kill.", prefix = false)
+ arachneKillTime = Duration.ZERO
+ arachneSpawnedTime = SimpleTimeMark.farPast()
+ }
+ }
+
+ @SubscribeEvent
+ fun onWorldChange(event: LorenzWorldChangeEvent) {
+ arachneSpawnedTime = SimpleTimeMark.farPast()
+ }
+
+ fun isEnabled() = IslandType.SPIDER_DEN.isInIsland() && config.arachneKillTimer
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/MobHighlight.kt b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/MobHighlight.kt
index af439d767702..7c33afcdb653 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/MobHighlight.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/combat/mobs/MobHighlight.kt
@@ -125,7 +125,7 @@ object MobHighlight {
event.exactPlayerEyeLocation(),
arachne.getLorenzVec().add(y = 1),
LorenzColor.RED.toColor(),
- 5,
+ config.lineToArachneWidth,
true
)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt b/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt
index c60c84ae5bf5..e7b3333dd7eb 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/commands/PartyCommands.kt
@@ -4,11 +4,20 @@ import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
import at.hannibal2.skyhanni.data.FriendAPI
import at.hannibal2.skyhanni.data.PartyAPI
+import at.hannibal2.skyhanni.data.PartyAPI.partyLeader
+import at.hannibal2.skyhanni.data.PartyAPI.transferVoluntaryPattern
+import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.MessageSendToServerEvent
import at.hannibal2.skyhanni.features.misc.limbo.LimboTimeTracker
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.EntityUtils
import at.hannibal2.skyhanni.utils.HypixelCommands
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.StringUtils.trimWhiteSpace
+import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@SkyHanniModule
@@ -67,14 +76,29 @@ object PartyCommands {
HypixelCommands.partyPromote(args[0])
}
+ fun reverseTransfer() {
+ if (!config.reversePT.command) return
+ if (PartyAPI.partyMembers.isEmpty()) return
+ val prevPartyLeader = PartyAPI.prevPartyLeader ?: return
+
+ autoPartyTransfer(prevPartyLeader)
+ }
+
+ private fun autoPartyTransfer(prevPartyLeader: String) {
+ HypixelCommands.partyTransfer(prevPartyLeader)
+ config.reversePT.message?.let {
+ if (it.isNotBlank()) {
+ HypixelCommands.partyChat(it)
+ }
+ }
+ }
+
@SubscribeEvent
fun onMessageSendToServer(event: MessageSendToServerEvent) {
if (!config.partyKickReason) {
return
}
- if (!event.message.startsWith("/party kick ", ignoreCase = true)
- && !event.message.startsWith("/p kick ", ignoreCase = true)
- ) {
+ if (!event.message.startsWith("/party kick ", ignoreCase = true) && !event.message.startsWith("/p kick ", ignoreCase = true)) {
return
}
val args = event.message.substringAfter("kick").trim().split(" ")
@@ -116,6 +140,22 @@ object PartyCommands {
event.move(31, "commands", "misc.commands")
}
+
+ @SubscribeEvent(priority = EventPriority.LOW)
+ fun onChat(event: LorenzChatEvent) {
+ if (!config.reversePT.clickable) return
+ if (!transferVoluntaryPattern.matches(event.message.trimWhiteSpace().removeColor())) return
+ if (partyLeader != LorenzUtils.getPlayerName()) return
+
+ val prevPartyLeader = PartyAPI.prevPartyLeader ?: return
+ event.blockedReason = "replacing"
+
+ ChatUtils.clickableChat(
+ event.message,
+ onClick = { autoPartyTransfer(prevPartyLeader) },
+ prefix = false,
+ )
+ }
}
private val otherPartyCommands = listOf(
@@ -126,5 +166,5 @@ private val otherPartyCommands = listOf(
"Mute",
"Private",
"Warp",
- "Settings"
+ "Settings",
)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonAPI.kt
index f8d432613084..f95c846b2a0a 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonAPI.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonAPI.kt
@@ -1,9 +1,13 @@
package at.hannibal2.skyhanni.features.dungeon
+import at.hannibal2.skyhanni.data.ClickType
+import at.hannibal2.skyhanni.data.ClickedBlockType
import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.data.ProfileStorageData
import at.hannibal2.skyhanni.data.ScoreboardData
+import at.hannibal2.skyhanni.events.BlockClickEvent
import at.hannibal2.skyhanni.events.DebugDataCollectEvent
+import at.hannibal2.skyhanni.events.DungeonBlockClickEvent
import at.hannibal2.skyhanni.events.DungeonBossRoomEnterEvent
import at.hannibal2.skyhanni.events.DungeonCompleteEvent
import at.hannibal2.skyhanni.events.DungeonEnterEvent
@@ -14,6 +18,8 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.TablistFooterUpdateEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.BlockUtils
+import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
import at.hannibal2.skyhanni.utils.CollectionUtils.equalsOneOf
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
@@ -30,6 +36,7 @@ import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.TabListData
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
+import net.minecraft.init.Blocks
import net.minecraft.item.ItemStack
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@@ -37,13 +44,10 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
object DungeonAPI {
private val floorPattern = " §7⏣ §cThe Catacombs §7\\((?.*)\\)".toPattern()
- private val uniqueClassBonus =
- "^Your ([A-Za-z]+) stats are doubled because you are the only player using this class!$".toRegex()
+ private val uniqueClassBonus = "^Your ([A-Za-z]+) stats are doubled because you are the only player using this class!$".toRegex()
- private val bossPattern =
- "View all your (?\\w+) Collection".toPattern()
- private val levelPattern =
- " +(?\\d+).*".toPattern()
+ private val bossPattern = "View all your (?\\w+) Collection".toPattern()
+ private val levelPattern = " +(?\\d+).*".toPattern()
private val killPattern = " +☠ Defeated (?\\w+).*".toPattern()
private val totalKillsPattern = "§7Total Kills: §e(?.*)".toPattern()
@@ -58,6 +62,8 @@ object DungeonAPI {
val bossStorage: MutableMap? get() = ProfileStorageData.profileSpecific?.dungeons?.bosses
private val patternGroup = RepoPattern.group("dungeon")
+ private const val WITHER_ESSENCE_TEXTURE =
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzRkYjRhZGZhOWJmNDhmZjVkNDE3MDdhZTM0ZWE3OGJkMjM3MTY1OWZjZDhjZDg5MzQ3NDlhZjRjY2U5YiJ9fX0="
/**
* REGEX-TEST: Time Elapsed: §a01m 17s
@@ -68,9 +74,13 @@ object DungeonAPI {
"Time Elapsed: §.(?:(?\\d+)m )?(?\\d+)s",
)
+ /**
+ * REGEX-TEST: §f §r§cMaster Mode The Catacombs §r§8- §r§eFloor VII
+ * REGEX-TEST: §f §r§cThe Catacombs §r§8- §r§eFloor V
+ */
private val dungeonComplete by patternGroup.pattern(
"complete",
- "§.\\s+§.§.(?:The|Master Mode) Catacombs §.§.- §.§.(?:Floor )?(?M?[IV]{1,3}|Entrance)",
+ "§.\\s+§.§.(?:Master Mode )?The Catacombs §.§.- §.§.(?:Floor )?(?M?[IV]{1,3}|Entrance)",
)
private val dungeonRoomPattern by patternGroup.pattern(
"room",
@@ -145,7 +155,6 @@ object DungeonAPI {
group("roomId")
}
-
fun getColor(level: Int): String = when {
level >= 50 -> "§c§l"
level >= 45 -> "§c"
@@ -170,10 +179,9 @@ object DungeonAPI {
}
}
if (dungeonFloor != null && playerClass == null) {
- val playerTeam =
- TabListData.getTabList().firstOrNull {
- it.contains(LorenzUtils.getPlayerName())
- }?.removeColor() ?: ""
+ val playerTeam = TabListData.getTabList().firstOrNull {
+ it.contains(LorenzUtils.getPlayerName())
+ }?.removeColor() ?: ""
for (dungeonClass in DungeonClass.entries) {
if (playerTeam.contains("(${dungeonClass.scoreboardName} ")) {
@@ -351,4 +359,27 @@ object DungeonAPI {
fun getByInventoryName(inventory: String) = entries.firstOrNull { it.inventory == inventory }
}
}
+
+ @SubscribeEvent
+ fun onBlockClick(event: BlockClickEvent) {
+ if (!inDungeon() || event.clickType != ClickType.RIGHT_CLICK) return
+
+ val position = event.position
+ val blockType: ClickedBlockType = when (position.getBlockAt()) {
+ Blocks.chest -> ClickedBlockType.CHEST
+ Blocks.trapped_chest -> ClickedBlockType.TRAPPED_CHEST
+ Blocks.lever -> ClickedBlockType.LEVER
+ Blocks.skull -> {
+ val blockTexture = BlockUtils.getTextureFromSkull(position.toBlockPos())
+ if (blockTexture == WITHER_ESSENCE_TEXTURE) {
+ ClickedBlockType.WITHER_ESSENCE
+ } else {
+ return
+ }
+ }
+
+ else -> return
+ }
+ DungeonBlockClickEvent(position, blockType).post()
+ }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt
index 93efa204fd7b..508296d4974c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonFinderFeatures.kt
@@ -31,64 +31,64 @@ object DungeonFinderFeatures {
private val patternGroup = RepoPattern.group("dungeon.finder")
private val pricePattern by patternGroup.pattern(
"price",
- "(?i).*([0-9]{2,3}K|[0-9]{1,3}M|[0-9]+\\.[0-9]M|[0-9] ?MIL).*"
+ "(?i).*([0-9]{2,3}K|[0-9]{1,3}M|[0-9]+\\.[0-9]M|[0-9] ?MIL).*",
)
private val carryPattern by patternGroup.pattern(
"carry",
- "(?i).*(CARRY|CARY|CARRIES|CARIES|COMP|TO CATA [0-9]{2}).*"
+ "(?i).*(CARRY|CARY|CARRIES|CARIES|COMP|TO CATA [0-9]{2}).*",
)
private val nonPugPattern by patternGroup.pattern(
"nonpug",
- "(?i).*(PERM|VC|DISCORD).*"
+ "(?i).*(PERM|VC|DISCORD).*",
)
private val memberPattern by patternGroup.pattern(
"member",
- ".*§.(?.*)§f: §e(?.*)§b \\(§e(?.*)§b\\)"
+ ".*§.(?.*)§f: §e(?.*)§b \\(§e(?.*)§b\\)",
)
private val ineligiblePattern by patternGroup.pattern(
"ineligible",
- "§c(Requires .*$|You don't meet the requirement!|Complete previous floor first!$)"
+ "§c(Requires .*$|You don't meet the requirement!|Complete previous floor first!$)",
)
private val classLevelPattern by patternGroup.pattern(
"class.level",
- " §.(?.*)§f: §e(?.*)§b \\(§e(?.*)§b\\)"
+ " §.(?.*)§f: §e(?.*)§b \\(§e(?.*)§b\\)",
)
private val notePattern by patternGroup.pattern(
"note",
- "§7§7Note: §f(?.*)"
+ "§7§7Note: §f(?.*)",
)
/**
* REGEX-TEST: The Catacombs
- * REGEX-TEST: Master Mode The Catacombs
+ * REGEX-TEST: MM The Catacombs
*/
private val floorTypePattern by patternGroup.pattern(
"floor.type",
- "(The Catacombs).*|.*(Master Mode The Catacombs).*",
+ "(The Catacombs).*|.*(MM The Catacombs).*",
)
private val checkIfPartyPattern by patternGroup.pattern(
"check.if.party",
- ".*('s Party)"
+ ".*('s Party)",
)
private val partyFinderTitlePattern by patternGroup.pattern(
"party.finder.title",
- "(Party Finder)"
+ "(Party Finder)",
)
private val catacombsGatePattern by patternGroup.pattern(
"catacombs.gate",
- "(Catacombs Gate)"
+ "(Catacombs Gate)",
)
private val selectFloorPattern by patternGroup.pattern(
"select.floor",
- "(Select Floor)"
+ "(Select Floor)",
)
private val entranceFloorPattern by patternGroup.pattern(
"entrance",
- "(.*Entrance)"
+ "(.*Entrance)",
)
private val floorPattern by patternGroup.pattern(
"floor",
- "(Floor .*)"
+ "(Floor .*)",
)
private val anyFloorPattern by patternGroup.pattern(
"floor.any",
@@ -97,31 +97,31 @@ object DungeonFinderFeatures {
/**
* REGEX-TEST: Master Mode The Catacombs
- * REGEX-TEST: The Catacombs
+ * REGEX-TEST: MM The Catacombs
*/
private val masterModeFloorPattern by patternGroup.pattern(
"floor.mastermode",
- "(MM )|(.*Master Mode The Catacombs)"
+ "(MM|.*Master Mode) The Catacombs.*",
)
private val dungeonFloorPattern by patternGroup.pattern(
"floor.dungeon",
- "(Dungeon: .*)"
+ "(Dungeon: .*)",
)
private val floorFloorPattern by patternGroup.pattern(
"floor.pattern",
- "(Floor: .*)"
+ "(Floor: .*)",
)
private val floorNumberPattern by patternGroup.pattern(
"floor.number",
- ".* (?[IV\\d]+)"
+ ".* (?[IV\\d]+)",
)
private val getDungeonClassPattern by patternGroup.pattern(
"get.dungeon.class",
- ".* (?.*)"
+ ".* (?.*)",
)
private val detectDungeonClassPattern by patternGroup.pattern(
"detect.dungeon.class",
- "§7View and select a dungeon class."
+ "§7View and select a dungeon class.",
)
private val allowedSlots = (10..34).filter { it !in listOf(17, 18, 26, 27) }
@@ -205,14 +205,13 @@ object DungeonFinderFeatures {
}
}
- private fun getFloorName(floor: String, dungeon: String, floorNum: Int?): String =
- if (entranceFloorPattern.matches(floor)) {
- "E"
- } else if (masterModeFloorPattern.matches(dungeon)) {
- "M$floorNum"
- } else {
- "F$floorNum"
- }
+ private fun getFloorName(floor: String, dungeon: String, floorNum: Int?): String = if (entranceFloorPattern.matches(floor)) {
+ "E"
+ } else if (masterModeFloorPattern.matches(dungeon)) {
+ "M$floorNum"
+ } else {
+ "F$floorNum"
+ }
private fun highlightingHandler(event: InventoryOpenEvent): Map {
val map = mutableMapOf()
@@ -307,6 +306,10 @@ object DungeonFinderFeatures {
fun onTooltip(event: LorenzToolTipEvent) {
if (!isEnabled()) return
if (!inInventory) return
+
+ val featureActive = config.let { it.coloredClassLevel || it.showMissingClasses }
+ if (!featureActive) return
+
val toolTip = toolTipMap[event.slot.slotNumber]
if (toolTip.isNullOrEmpty()) return
// TODO @Thunderblade73 fix that to "event.toolTip = toolTip"
@@ -326,8 +329,7 @@ object DungeonFinderFeatures {
if (!config.floorAsStackSize) return
val slot = event.slot
if (slot.slotNumber != slot.slotIndex) return
- event.stackTip = (floorStackSize[slot.slotIndex]
- ?.takeIf { it.isNotEmpty() } ?: return)
+ event.stackTip = (floorStackSize[slot.slotIndex]?.takeIf { it.isNotEmpty() } ?: return)
}
@SubscribeEvent
@@ -335,11 +337,9 @@ object DungeonFinderFeatures {
if (!isEnabled()) return
if (!inInventory) return
- event.gui.inventorySlots.inventorySlots
- .associateWith { highlightParty[it.slotNumber] }
- .forEach { (slot, color) ->
- color?.let { slot.highlight(it) }
- }
+ event.gui.inventorySlots.inventorySlots.associateWith { highlightParty[it.slotNumber] }.forEach { (slot, color) ->
+ color?.let { slot.highlight(it) }
+ }
}
@SubscribeEvent
diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonHighlightClickedBlocks.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonHighlightClickedBlocks.kt
index 16b5cb5f666a..4d494567d4d6 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonHighlightClickedBlocks.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonHighlightClickedBlocks.kt
@@ -1,96 +1,115 @@
package at.hannibal2.skyhanni.features.dungeon
import at.hannibal2.skyhanni.SkyHanniMod
-import at.hannibal2.skyhanni.data.ClickType
-import at.hannibal2.skyhanni.events.BlockClickEvent
+import at.hannibal2.skyhanni.api.event.HandleEvent
+import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
+import at.hannibal2.skyhanni.data.ClickedBlockType
+import at.hannibal2.skyhanni.events.DungeonBlockClickEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.BlockUtils
-import at.hannibal2.skyhanni.utils.BlockUtils.getBlockAt
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
+import at.hannibal2.skyhanni.utils.ExtendedChatColor
import at.hannibal2.skyhanni.utils.LorenzColor
import at.hannibal2.skyhanni.utils.LorenzVec
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.RenderUtils.drawColor
import at.hannibal2.skyhanni.utils.RenderUtils.drawString
-import net.minecraft.init.Blocks
+import at.hannibal2.skyhanni.utils.TimeLimitedCache
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+import kotlin.time.Duration.Companion.seconds
@SkyHanniModule
object DungeonHighlightClickedBlocks {
- private val blocks = mutableListOf()
- private var colorIndex = 0
- private val colors = listOf(LorenzColor.YELLOW, LorenzColor.AQUA, LorenzColor.GREEN, LorenzColor.LIGHT_PURPLE)
+ private val config get() = SkyHanniMod.feature.dungeon.clickedBlocks
+
+ private val patternGroup = RepoPattern.group("dungeons.highlightclickedblock")
+ private val leverPattern by patternGroup.pattern(
+ "lever",
+ "§cYou hear the sound of something opening...",
+ )
+ private val lockedPattern by patternGroup.pattern(
+ "locked",
+ "§cThat chest is locked!",
+ )
- private fun getNextColor(): LorenzColor {
+ private val blocks = TimeLimitedCache(3.seconds)
+ private var colorIndex = 0
+ private val undesirableColors = listOf(
+ LorenzColor.BLACK,
+ LorenzColor.WHITE,
+ LorenzColor.CHROMA,
+ LorenzColor.GRAY,
+ LorenzColor.DARK_GRAY,
+ )
+ private val randomColors = LorenzColor.entries.filter { it !in undesirableColors }
+
+ private fun getRandomColor(): LorenzColor {
var id = colorIndex + 1
- if (id == colors.size) id = 0
+ if (id == randomColors.size) id = 0
colorIndex = id
- return colors[colorIndex]
+ return randomColors[colorIndex]
}
@SubscribeEvent
fun onChat(event: LorenzChatEvent) {
- if (!SkyHanniMod.feature.dungeon.highlightClickedBlocks) return
- if (!DungeonAPI.inDungeon()) return
+ if (!isEnabled()) return
- if (event.message == "§cYou hear the sound of something opening...") {
+ if (leverPattern.matches(event.message)) {
event.blockedReason = "dungeon_highlight_clicked_block"
}
- }
- @SubscribeEvent
- fun onBlockClick(event: BlockClickEvent) {
- if (!SkyHanniMod.feature.dungeon.highlightClickedBlocks) return
- if (!DungeonAPI.inDungeon()) return
- if (DungeonAPI.inBossRoom) return
- if (event.clickType != ClickType.RIGHT_CLICK) return
-
- val position = event.position
- if (blocks.any { it.position == position }) return
-
- val type: ClickedBlockType = when (position.getBlockAt()) {
- Blocks.chest, Blocks.trapped_chest -> ClickedBlockType.CHEST
- Blocks.lever -> ClickedBlockType.LEVER
- Blocks.skull -> ClickedBlockType.WITHER_ESSENCE
- else -> return
+ if (lockedPattern.matches(event.message)) {
+ blocks.lastOrNull { it.value.displayText.contains("Chest") }?.value?.color = config.lockedChestColor.toChromaColor()
}
+ }
- if (type == ClickedBlockType.WITHER_ESSENCE) {
- val text = BlockUtils.getTextureFromSkull(position.toBlockPos())
- if (text != "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQ" +
- "ubmV0L3RleHR1cmUvYzRkYjRhZGZhOWJmNDhmZjVkNDE3M" +
- "DdhZTM0ZWE3OGJkMjM3MTY1OWZjZDhjZDg5MzQ3NDlhZjRjY2U5YiJ9fX0="
- ) {
- return
- }
- }
+ @HandleEvent
+ fun onDungeonClickedBlock(event: DungeonBlockClickEvent) {
+ if (!isEnabled()) return
+ val isWaterRoom = DungeonAPI.getRoomID() == "-60,-60"
+ if (isWaterRoom && event.blockType == ClickedBlockType.LEVER) return
+
+ val type = event.blockType
- val inWaterRoom = DungeonAPI.getRoomID() == "-60,-60"
- if (inWaterRoom && type == ClickedBlockType.LEVER) return
+ val color = if (config.randomColor) getRandomColor().toColor() else getBlockProperties(type).color.toChromaColor()
+ val displayText = ExtendedChatColor(color.rgb, false).toString() + "Clicked " + getBlockProperties(type).name
+ blocks[event.position] = ClickedBlock(displayText, color)
- val color = getNextColor()
- val displayText = color.getChatColor() + "Clicked " + type.display
- blocks.add(ClickedBlock(position, displayText, color, System.currentTimeMillis()))
+ }
+
+ private fun getBlockProperties(type: ClickedBlockType): BlockProperties {
+ return when (type) {
+ ClickedBlockType.LEVER -> BlockProperties("Lever", config.leverColor)
+ ClickedBlockType.CHEST -> BlockProperties("Chest", config.chestColor)
+ ClickedBlockType.TRAPPED_CHEST -> BlockProperties("Trapped Chest", config.trappedChestColor)
+ ClickedBlockType.WITHER_ESSENCE -> BlockProperties("Wither Essence", config.witherEssenceColor)
+ }
}
@SubscribeEvent
fun onWorldRender(event: LorenzRenderWorldEvent) {
- if (!SkyHanniMod.feature.dungeon.highlightClickedBlocks) return
- if (!DungeonAPI.inDungeon()) return
+ if (!isEnabled()) return
- blocks.removeAll { System.currentTimeMillis() > it.time + 3000 }
- blocks.forEach {
- event.drawColor(it.position, it.color)
- event.drawString(it.position.add(0.5, 0.5, 0.5), it.displayText, true)
+ blocks.forEach { (position, block) ->
+ event.drawColor(position, block.color)
+ if (config.showText) {
+ event.drawString(position.add(0.5, 0.5, 0.5), block.displayText, true)
+ }
}
}
- class ClickedBlock(val position: LorenzVec, val displayText: String, val color: LorenzColor, val time: Long)
-
- enum class ClickedBlockType(val display: String) {
- LEVER("Lever"),
- CHEST("Chest"),
- WITHER_ESSENCE("Wither Essence"),
+ @SubscribeEvent
+ fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
+ event.move(56, "dungeon.highlightClickedBlocks", "dungeon.clickedBlocks.enabled")
}
+
+ class ClickedBlock(val displayText: String, var color: Color)
+ class BlockProperties(val name: String, val color: String)
+
+ fun isEnabled() = !DungeonAPI.inBossRoom && DungeonAPI.inDungeon() && config.enabled
+
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonMobManager.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonMobManager.kt
new file mode 100644
index 000000000000..46450a5645d5
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonMobManager.kt
@@ -0,0 +1,149 @@
+package at.hannibal2.skyhanni.features.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.data.mob.Mob
+import at.hannibal2.skyhanni.data.mob.MobData
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
+import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
+import at.hannibal2.skyhanni.events.LorenzTickEvent
+import at.hannibal2.skyhanni.events.MobEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
+import at.hannibal2.skyhanni.utils.ConditionalUtils.onToggle
+import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
+import at.hannibal2.skyhanni.utils.RenderUtils.draw3DLine
+import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled
+import at.hannibal2.skyhanni.utils.RenderUtils.exactPlayerEyeLocation
+import at.hannibal2.skyhanni.utils.getLorenzVec
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import java.awt.Color
+
+@SkyHanniModule
+object DungeonMobManager {
+
+ private val config get() = SkyHanniMod.feature.dungeon.objectHighlighter
+ private val starredConfig get() = config.starred
+ private val fel get() = config.fel
+
+ private val staredInvisible = mutableSetOf()
+ private val felOnTheGround = mutableSetOf()
+
+ @SubscribeEvent
+ fun onConfigLoad(event: ConfigLoadEvent) {
+ onToggle(
+ starredConfig.highlight,
+ starredConfig.colour,
+ ) {
+ val color = if (starredConfig.highlight.get()) getStarColor() else null
+ MobData.skyblockMobs.filter { it.hasStar }.forEach {
+ handleStar0(it, color)
+ }
+ if (!starredConfig.highlight.get()) {
+ staredInvisible.clear()
+ }
+ }
+ onToggle(
+ fel.highlight,
+ fel.colour,
+ ) {
+ if (fel.highlight.get()) {
+ if (felOnTheGround.isEmpty()) {
+ MobData.skyblockMobs.forEach(::handleFel)
+ }
+ } else {
+ felOnTheGround.clear()
+ }
+ }
+ }
+
+ @SubscribeEvent
+ fun onMobSpawn(event: MobEvent.Spawn.SkyblockMob) {
+ if (event.mob.mobType != Mob.Type.DUNGEON) return
+ handleStar(event.mob)
+ handleFel(event.mob)
+ }
+
+ @SubscribeEvent
+ fun onMobDeSpawn(event: MobEvent.DeSpawn.SkyblockMob) {
+ if (event.mob.mobType != Mob.Type.DUNGEON) return
+ if (starredConfig.highlight.get()) {
+ staredInvisible.remove(event.mob)
+ }
+ handleFelDespawn(event.mob)
+ }
+
+ @SubscribeEvent
+ fun onLorenzTick(event: LorenzTickEvent) {
+ if (!IslandType.CATACOMBS.isInIsland()) return
+ handleInvisibleStar()
+ }
+
+ @SubscribeEvent
+ fun onRenderWorld(event: LorenzRenderWorldEvent) {
+ if (!fel.highlight.get()) return
+ if (fel.line) {
+ felOnTheGround.filter { it.canBeSeen() }.forEach {
+ event.draw3DLine(
+ it.baseEntity.getLorenzVec().add(y = 0.15),
+ event.exactPlayerEyeLocation(),
+ fel.colour.get().toChromaColor(),
+ 3,
+ true,
+ )
+ }
+ }
+
+ felOnTheGround.removeIf { mob ->
+ event.drawWaypointFilled(
+ mob.baseEntity.getLorenzVec().add(-0.5, -0.23, -0.5),
+ fel.colour.get().toChromaColor(),
+ seeThroughBlocks = false,
+ beacon = false,
+ extraSize = -0.2,
+ minimumAlpha = 0.8f,
+ inverseAlphaScale = true,
+ )
+ !mob.isInvisible()
+ }
+ }
+
+ private fun handleStar(mob: Mob) {
+ if (!starredConfig.highlight.get()) return
+ if (!mob.hasStar) return
+ handleStar0(mob, getStarColor())
+ }
+
+ private fun handleInvisibleStar() {
+ if (!starredConfig.highlight.get()) return
+ staredInvisible.removeIf {
+ val visible = !it.isInvisible()
+ if (visible) {
+ it.highlight(getStarColor())
+ }
+ visible
+ }
+ }
+
+ private fun getStarColor(): Color = starredConfig.colour.get().toChromaColor()
+
+ private fun handleStar0(mob: Mob, colour: Color?) {
+ if (mob.isInvisible()) {
+ staredInvisible.add(mob)
+ return
+ }
+ mob.highlight(colour)
+ }
+
+ private fun handleFel(mob: Mob) {
+ if (!fel.highlight.get()) return
+ if (mob.name != "Fels") return
+ if (!mob.isInvisible()) return
+ felOnTheGround.add(mob)
+ }
+
+ private fun handleFelDespawn(mob: Mob) {
+ if (!fel.highlight.get()) return
+ felOnTheGround.remove(mob)
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonSecretChime.kt b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonSecretChime.kt
new file mode 100644
index 000000000000..a19b60a13cce
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/dungeon/DungeonSecretChime.kt
@@ -0,0 +1,38 @@
+package at.hannibal2.skyhanni.features.dungeon
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.event.HandleEvent
+import at.hannibal2.skyhanni.data.ClickedBlockType
+import at.hannibal2.skyhanni.events.DungeonBlockClickEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.SoundUtils.playSound
+
+@SkyHanniModule
+object DungeonSecretChime {
+ private val config get() = SkyHanniMod.feature.dungeon.secretChime
+
+ @HandleEvent
+ fun onDungeonClickedBlock(event: DungeonBlockClickEvent) {
+ if (!isEnabled()) return
+ val isWaterRoom = DungeonAPI.getRoomID() == "-60,-60"
+ if (isWaterRoom && event.blockType == ClickedBlockType.LEVER) return
+
+ when (event.blockType) {
+ ClickedBlockType.CHEST,
+ ClickedBlockType.TRAPPED_CHEST,
+ ClickedBlockType.LEVER,
+ ClickedBlockType.WITHER_ESSENCE,
+ -> playSound()
+ }
+ }
+
+ fun isEnabled() = !DungeonAPI.inBossRoom && DungeonAPI.inDungeon() && config.enabled
+
+ @JvmStatic
+ fun playSound() {
+ with(config) {
+ SoundUtils.createSound(soundName, soundPitch, 100f).playSound()
+ }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt
index bf362d918672..34dab01e6ce1 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/BurrowWarpHelper.kt
@@ -137,6 +137,7 @@ object BurrowWarpHelper {
DA("Dark Auction", LorenzVec(91, 74, 173), 2),
MUSEUM("Museum", LorenzVec(-75, 76, 81), 2),
WIZARD("Wizard", LorenzVec(42.5, 122.0, 69.0), 5, { config.ignoredWarps.wizard }),
+ STONKS("Stonks", LorenzVec(-52.5, 70.0, -49.5), 5, { config.ignoredWarps.stonks }),
;
fun distance(other: LorenzVec): Double = other.distance(location) + extraBlocks
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt
index 1ba2724c3669..ade5966eaaa6 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/DianaProfitTracker.kt
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.features.event.diana
import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.ItemAddManager
import at.hannibal2.skyhanni.data.jsonobjects.repo.DianaDropsJson
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.ItemAddEvent
@@ -8,7 +9,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
@@ -18,6 +19,8 @@ import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.renderables.Searchable
+import at.hannibal2.skyhanni.utils.renderables.toSearchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData
import at.hannibal2.skyhanni.utils.tracker.SkyHanniItemTracker
@@ -33,17 +36,18 @@ object DianaProfitTracker {
private val patternGroup = RepoPattern.group("diana.chat")
private val chatDugOutPattern by patternGroup.pattern(
"burrow.dug",
- "(§eYou dug out a Griffin Burrow!|§eYou finished the Griffin burrow chain!) .*"
+ "(§eYou dug out a Griffin Burrow!|§eYou finished the Griffin burrow chain!) .*",
)
private val chatDugOutCoinsPattern by patternGroup.pattern(
"coins",
- "§6§lWow! §r§eYou dug out §r§6(?.*) coins§r§e!"
+ "§6§lWow! §r§eYou dug out §r§6(?.*) coins§r§e!",
)
private val tracker = SkyHanniItemTracker(
"Diana Profit Tracker",
{ Data() },
- { it.diana.dianaProfitTracker }) { drawDisplay(it) }
+ { it.diana.dianaProfitTracker },
+ ) { drawDisplay(it) }
class Data : ItemTrackerData() {
@@ -70,25 +74,25 @@ object DianaProfitTracker {
val burrowDugCoinsFormat = item.totalAmount.shortFormat()
return listOf(
"§7Digging treasures gave you",
- "§6$burrowDugCoinsFormat coins §7in total."
+ "§6$burrowDugCoinsFormat coins §7in total.",
)
}
}
- private fun drawDisplay(data: Data): List> = buildList {
- addAsSingletonList("§e§lDiana Profit Tracker")
+ private fun drawDisplay(data: Data): List = buildList {
+ addSearchString("§e§lDiana Profit Tracker")
val profit = tracker.drawItems(data, { true }, this)
val treasureCoins = data.burrowsDug
- addAsSingletonList(
+ add(
Renderable.hoverTips(
"§7Burrows dug: §e${treasureCoins.addSeparators()}",
- listOf("§7You dug out griffin burrows §e${treasureCoins.addSeparators()} §7times.")
- )
+ listOf("§7You dug out griffin burrows §e${treasureCoins.addSeparators()} §7times."),
+ ).toSearchable(),
)
- addAsSingletonList(tracker.addTotalProfit(profit, data.burrowsDug, "burrow"))
+ add(tracker.addTotalProfit(profit, data.burrowsDug, "burrow"))
tracker.addPriceFromButton(this)
}
@@ -97,14 +101,16 @@ object DianaProfitTracker {
fun onItemAdd(event: ItemAddEvent) {
if (!isEnabled()) return
- val internalName = event.internalName
+ tryAddItem(event.internalName, event.amount, event.source == ItemAddManager.Source.COMMAND)
+ }
- if (!isAllowedItem(internalName)) {
+ private fun tryAddItem(internalName: NEUInternalName, amount: Int, command: Boolean) {
+ if (!isAllowedItem(internalName) && internalName != NEUInternalName.SKYBLOCK_COIN) {
ChatUtils.debug("Ignored non-diana item pickup: '$internalName'")
return
}
- tracker.addItem(internalName, event.amount)
+ tracker.addItem(internalName, amount, command)
}
@SubscribeEvent
@@ -119,7 +125,7 @@ object DianaProfitTracker {
}
chatDugOutCoinsPattern.matchMatcher(message) {
BurrowAPI.lastBurrowRelatedChatMessage = SimpleTimeMark.now()
- tracker.addCoins(group("coins").formatInt())
+ tryAddItem(NEUInternalName.SKYBLOCK_COIN, group("coins").formatInt(), command = false)
tryHide(event)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt
index 68b70a77adf1..ec882d6ecd74 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/diana/MythologicalCreatureTracker.kt
@@ -5,14 +5,15 @@ import at.hannibal2.skyhanni.events.ConfigLoadEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.CollectionUtils.sumAllValues
import at.hannibal2.skyhanni.utils.ConditionalUtils
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.renderables.Searchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
import at.hannibal2.skyhanni.utils.tracker.TrackerData
@@ -29,32 +30,31 @@ object MythologicalCreatureTracker {
private val patternGroup = RepoPattern.group("event.diana.mythological.tracker")
private val minotaurPattern by patternGroup.pattern(
"minotaur",
- ".* §r§eYou dug out a §r§2Minotaur§r§e!"
+ ".* §r§eYou dug out a §r§2Minotaur§r§e!",
)
private val gaiaConstructPattern by patternGroup.pattern(
"gaiaconstruct",
- ".* §r§eYou dug out a §r§2Gaia Construct§r§e!"
+ ".* §r§eYou dug out a §r§2Gaia Construct§r§e!",
)
private val minosChampionPattern by patternGroup.pattern(
"minoschampion",
- ".* §r§eYou dug out a §r§2Minos Champion§r§e!"
+ ".* §r§eYou dug out a §r§2Minos Champion§r§e!",
)
private val siameseLynxesPattern by patternGroup.pattern(
"siameselynxes",
- ".* §r§eYou dug out §r§2Siamese Lynxes§r§e!"
+ ".* §r§eYou dug out §r§2Siamese Lynxes§r§e!",
)
private val minosHunterPattern by patternGroup.pattern(
"minoshunter",
- ".* §r§eYou dug out a §r§2Minos Hunter§r§e!"
+ ".* §r§eYou dug out a §r§2Minos Hunter§r§e!",
)
private val minosInquisitorPattern by patternGroup.pattern(
"minosinquisitor",
- ".* §r§eYou dug out a §r§2Minos Inquisitor§r§e!"
+ ".* §r§eYou dug out a §r§2Minos Inquisitor§r§e!",
)
private val tracker =
- SkyHanniTracker("Mythological Creature Tracker", { Data() }, { it.diana.mythologicalMobTracker })
- { drawDisplay(it) }
+ SkyHanniTracker("Mythological Creature Tracker", { Data() }, { it.diana.mythologicalMobTracker }) { drawDisplay(it) }
class Data : TrackerData() {
@@ -89,8 +89,7 @@ object MythologicalCreatureTracker {
// TODO migrate to abstract feature in the future
if (creatureType == MythologicalCreatureType.MINOS_INQUISITOR) {
- event.chatComponent =
- ChatComponentText(event.message + " §e(${it.creaturesSinceLastInquisitor})")
+ event.chatComponent = ChatComponentText(event.message + " §e(${it.creaturesSinceLastInquisitor})")
it.creaturesSinceLastInquisitor = 0
} else it.creaturesSinceLastInquisitor++
}
@@ -99,8 +98,8 @@ object MythologicalCreatureTracker {
}
}
- private fun drawDisplay(data: Data): List> = buildList {
- addAsSingletonList("§7Mythological Creature Tracker:")
+ private fun drawDisplay(data: Data): List = buildList {
+ addSearchString("§7Mythological Creature Tracker:")
val total = data.count.sumAllValues()
for ((creatureType, amount) in data.count.entries.sortedByDescending { it.value }) {
val percentageSuffix = if (config.showPercentage.get()) {
@@ -108,10 +107,13 @@ object MythologicalCreatureTracker {
" §7$percentage"
} else ""
- addAsSingletonList(" §7- §e${amount.addSeparators()} ${creatureType.displayName}$percentageSuffix")
+ addSearchString(
+ " §7- §e${amount.addSeparators()} ${creatureType.displayName}$percentageSuffix",
+ searchText = creatureType.displayName,
+ )
}
- addAsSingletonList(" §7- §e${total.addSeparators()} §7Total Mythological Creatures")
- addAsSingletonList(" §7- §e${data.creaturesSinceLastInquisitor.addSeparators()} §7Creatures since last Minos Inquisitor")
+ addSearchString(" §7- §e${total.addSeparators()} §7Total Mythological Creatures")
+ addSearchString(" §7- §e${data.creaturesSinceLastInquisitor.addSeparators()} §7Creatures since last Minos Inquisitor")
}
@SubscribeEvent
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt
new file mode 100644
index 000000000000..5c63fe3a800a
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityAPI.kt
@@ -0,0 +1,175 @@
+package at.hannibal2.skyhanni.features.event.hoppity
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.events.GuiContainerEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType
+import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getLore
+import at.hannibal2.skyhanni.utils.ItemUtils.itemName
+import at.hannibal2.skyhanni.utils.LorenzRarity
+import at.hannibal2.skyhanni.utils.LorenzRarity.DIVINE
+import at.hannibal2.skyhanni.utils.RegexUtils.firstMatcher
+import at.hannibal2.skyhanni.utils.RegexUtils.groupOrNull
+import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
+import at.hannibal2.skyhanni.utils.SkyblockSeason
+import net.minecraft.util.ChatComponentText
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+@SkyHanniModule
+object HoppityAPI {
+
+ private var hoppityEggChat = mutableListOf()
+ private var duplicate = false
+ private var lastRarity = ""
+ private var lastName = ""
+ private var newRabbit = false
+ private var lastChatMeal: HoppityEggType? = null
+ private var lastDuplicateAmount: Long? = null
+ private var rabbitBought = false
+
+ val hoppityRarities by lazy { LorenzRarity.entries.filter { it <= DIVINE } }
+
+ private fun resetChatData() {
+ this.hoppityEggChat = mutableListOf()
+ this.duplicate = false
+ this.newRabbit = false
+ this.lastRarity = ""
+ this.lastName = ""
+ this.lastChatMeal = null
+ this.lastDuplicateAmount = null
+ this.rabbitBought = false
+ }
+
+ fun isHoppityEvent() = (SkyblockSeason.currentSeason == SkyblockSeason.SPRING || SkyHanniMod.feature.dev.debug.alwaysHoppitys)
+
+ fun rarityByRabbit(rabbit: String): LorenzRarity? = hoppityRarities.firstOrNull { it.chatColorCode == rabbit.substring(0, 2) }
+
+ /**
+ * REGEX-TEST: §f1st Chocolate Milestone
+ * REGEX-TEST: §915th Chocolate Milestone
+ * REGEX-TEST: §622nd Chocolate Milestone
+ */
+ private val milestoneNamePattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "rabbit.milestone",
+ "(?:§.)*?(?\\d{1,2})[a-z]{2} Chocolate Milestone",
+ )
+
+ /**
+ * REGEX-TEST: §7Reach §6300B Chocolate §7all-time to
+ * REGEX-TEST: §7Reach §61k Chocolate §7all-time to unlock
+ */
+ private val allTimeLorePattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "milestone.alltime",
+ "§7Reach §6(?[\\d.MBk]*) Chocolate §7all-time.*",
+ )
+
+ /**
+ * REGEX-TEST: §7Spend §6150B Chocolate §7in the
+ * REGEX-TEST: §7Spend §62M Chocolate §7in the §6Chocolate
+ */
+ private val shopLorePattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "milestone.shop",
+ "§7Spend §6(?[\\d.MBk]*) Chocolate §7in.*",
+ )
+
+ fun fireSideDishMessage() {
+ LorenzChatEvent(
+ "§d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d!",
+ ChatComponentText(""),
+ ).postAndCatch()
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGH)
+ fun onSlotClick(event: GuiContainerEvent.SlotClickEvent) {
+ val index = event.slot?.slotIndex ?: return
+ if (index == -999) return
+
+ val clickedStack = InventoryUtils.getItemsInOpenChest()
+ .find { it.slotNumber == event.slot.slotNumber && it.hasStack }
+ ?.stack ?: return
+ val nameText = (if (clickedStack.hasDisplayName()) clickedStack.displayName else clickedStack.itemName)
+
+ milestoneNamePattern.matchMatcher(nameText) {
+ val itemLore = clickedStack.getLore()
+ if (!itemLore.any { it == "§eClick to claim!" }) return
+
+ // Will never match both all time and shop patterns together
+ allTimeLorePattern.firstMatcher(clickedStack.getLore()) {
+ LorenzChatEvent(
+ "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lChocolate Milestone Rabbit §r§din the Chocolate Factory§r§d!",
+ ChatComponentText(""),
+ ).postAndCatch()
+ }
+
+ shopLorePattern.firstMatcher(clickedStack.getLore()) {
+ LorenzChatEvent(
+ "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lShop Milestone Rabbit §r§din the Chocolate Factory§r§d!",
+ ChatComponentText(""),
+ ).postAndCatch()
+ }
+ }
+ }
+
+ // Dumbed down version of the Compact Chat for Hoppity's,
+ // with the additional native context of side dishes
+ fun handleChat(event: LorenzChatEvent) {
+ HoppityEggsManager.eggFoundPatterns.forEach {
+ it.matchMatcher(event.message) {
+ resetChatData()
+ lastChatMeal = getEggType(event)
+ attemptFire(event)
+ }
+ }
+
+ HoppityEggsManager.eggBoughtPattern.matchMatcher(event.message) {
+ if (group("rabbitname").equals(lastName)) {
+ rabbitBought = true
+ lastChatMeal = HoppityEggType.BOUGHT
+ attemptFire(event)
+ }
+ }
+
+ HoppityEggsManager.rabbitFoundPattern.matchMatcher(event.message) {
+ // The only cases where "You found ..." will come in with more than 1 message,
+ // or empty for hoppityEggChat, is where the rabbit was purchased from hoppity,
+ // In the case of buying, we want to reset variables to a clean state during this capture,
+ // as the important capture for the purchased message is the final message in
+ // the chain; "You found [rabbit]" -> "Dupe/New Rabbit" -> "You bought [rabbit]"
+ if ((hoppityEggChat.isEmpty() || hoppityEggChat.size > 1)) {
+ resetChatData()
+ }
+
+ lastName = group("name")
+ lastRarity = group("rarity")
+ attemptFire(event)
+ }
+
+ HoppityEggsManager.newRabbitFound.matchMatcher(event.message) {
+ newRabbit = true
+ groupOrNull("other")?.let {
+ attemptFire(event)
+ return
+ }
+ attemptFire(event)
+ }
+ }
+
+ fun attemptFire(event: LorenzChatEvent, lastDuplicateAmount: Long? = null) {
+ lastDuplicateAmount?.let {
+ this.lastDuplicateAmount = it
+ }
+ hoppityEggChat.add(event.message)
+ if (lastDuplicateAmount != null) {
+ this.duplicate = true
+ }
+ val lastChatMeal = lastChatMeal ?: return
+ if (hoppityEggChat.size == 3) {
+ RabbitFoundEvent(lastChatMeal, duplicate, lastName, lastDuplicateAmount ?: 0).post()
+ }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt
new file mode 100644
index 000000000000..57bc0975fac8
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCallWarning.kt
@@ -0,0 +1,155 @@
+package at.hannibal2.skyhanni.features.event.hoppity
+
+import at.hannibal2.skyhanni.events.ConfigLoadEvent
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.events.LorenzKeyPressEvent
+import at.hannibal2.skyhanni.events.SecondPassedEvent
+import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColorInt
+import at.hannibal2.skyhanni.utils.ConditionalUtils
+import at.hannibal2.skyhanni.utils.DelayedRun
+import at.hannibal2.skyhanni.utils.HypixelCommands
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
+import at.hannibal2.skyhanni.utils.SoundUtils
+import at.hannibal2.skyhanni.utils.StringUtils.isValidUuid
+import net.minecraft.client.Minecraft
+import net.minecraft.client.gui.Gui
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import org.lwjgl.input.Keyboard
+import java.time.Instant
+import kotlin.math.sin
+import kotlin.time.Duration.Companion.seconds
+
+@SkyHanniModule
+object HoppityCallWarning {
+
+ /**
+ * Test messages (and the real ones from Hypixel) have a space at the end of
+ * them that the IDE kills. So it's "§r§e ✆ "
+ *
+ * REGEX-TEST: §e✆ §r§bHoppity§r§e ✆
+ * REGEX-TEST: §e✆ §r§aHoppity§r§e ✆
+ */
+ private val initHoppityCallPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "hoppity.call.init",
+ "§e✆ §r(?:§a|§b)Hoppity§r§e ✆.*",
+ )
+
+ /**
+ * REGEX-TEST: §a✆ RING... §r §r§2§l[PICK UP]
+ * REGEX-TEST: §a✆ RING... RING... §r §r§2§l[PICK UP]
+ * REGEX-TEST: §a✆ RING... RING... RING... §r §r§2§l[PICK UP]
+ * REGEX-TEST: §a✆ RING... RING... RING...
+ */
+ private val callRingPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "hoppity.call.ring",
+ "§a✆ (?:RING\\.{3} ?){1,3}(?:§r §r§2§l\\[PICK UP])?",
+ )
+
+ /**
+ * REGEX-TEST: §e[NPC] §aHoppity§f: §b✆ §f§rWhat's up, §boBlazin§f?
+ */
+ private val pickupHoppityCallPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "hoppity.call.pickup",
+ "§e\\[NPC] §aHoppity§f: §b✆ §f§rWhat's up, .*§f\\?",
+ )
+
+ private val config get() = HoppityEggsManager.config.hoppityCallWarning
+ private var warningSound = SoundUtils.createSound("note.pling", 1f)
+ private var activeWarning = false
+ private var nextWarningTime: Instant? = null
+ private var finalWarningTime: Instant? = null
+ private val callLength = 7.seconds
+ private var acceptUUID: String? = null
+
+ @SubscribeEvent
+ fun onKeyPress(event: LorenzKeyPressEvent) {
+ if (config.acceptHotkey == Keyboard.KEY_NONE || config.acceptHotkey != event.keyCode) return
+ acceptUUID?.let {
+ HypixelCommands.callback(acceptUUID!!)
+ acceptUUID = null
+ }
+ }
+
+ @SubscribeEvent
+ fun onConfigLoad(event: ConfigLoadEvent) {
+ val soundProperty = config.hoppityCallSound
+ ConditionalUtils.onToggle(soundProperty) {
+ warningSound = SoundUtils.createSound(soundProperty.get(), 1f)
+ }
+ nextWarningTime = null
+ finalWarningTime = null
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ fun onChat(event: LorenzChatEvent) {
+ if (callRingPattern.matches(event.message) && acceptUUID == null) readPickupUuid(event)
+ if (!isEnabled()) return
+ if (initHoppityCallPattern.matches(event.message)) startWarningUser()
+ if (pickupHoppityCallPattern.matches(event.message)) stopWarningUser()
+ }
+
+ @SubscribeEvent
+ fun onTick(event: SecondPassedEvent) {
+ if (!isEnabled()) return
+ if (!activeWarning) return
+ if (nextWarningTime == null || finalWarningTime == null) return
+ val currentTime = Instant.now()
+ if (currentTime.isAfter(nextWarningTime)) {
+ SoundUtils.repeatSound(100, 10, warningSound)
+ nextWarningTime = currentTime.plusMillis(100)
+ }
+ if (currentTime >= finalWarningTime) stopWarningUser()
+ }
+
+ @SubscribeEvent
+ fun onRender(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!isEnabled() || !activeWarning) return
+ val minecraft = Minecraft.getMinecraft()
+ // Calculate a fluctuating alpha value based on the sine of time, for a smooth oscillation
+ val randomizationAlphaDouble = ((2 + sin(Instant.now().toEpochMilli().toDouble() / 1000)) * 255 / 4)
+ // Ensure the alpha value is an integer and within the valid range (0-255)
+ val randomizationAlphaInt = randomizationAlphaDouble.toInt().coerceIn(0..255)
+ // Shift the alpha value 24 bits to the left to position it in the color's alpha channel.
+ val shiftedRandomAlpha = randomizationAlphaInt shl 24
+ Gui.drawRect(
+ 0,
+ 0,
+ minecraft.displayWidth,
+ minecraft.displayHeight,
+ // Apply the shifted alpha and combine it with the RGB components of flashColor.
+ shiftedRandomAlpha or (config.flashColor.toChromaColorInt() and 0xFFFFFF),
+ )
+ GlStateManager.color(1F, 1F, 1F, 1F)
+ }
+
+ private fun readPickupUuid(event: LorenzChatEvent) {
+ val siblings = event.chatComponent.siblings.takeIf { it.size >= 3 } ?: return
+ val clickEvent = siblings[2]?.chatStyle?.chatClickEvent ?: return
+ if (clickEvent.action.name.lowercase() != "run_command" || !clickEvent.value.lowercase().startsWith("/cb")) return
+ acceptUUID = clickEvent.value.lowercase().replace("/cb ", "").takeIf { it.isValidUuid() }
+ if (acceptUUID != null) DelayedRun.runDelayed(12.seconds) { acceptUUID = null }
+ }
+
+ private fun startWarningUser() {
+ if (activeWarning) return
+ activeWarning = true
+ SoundUtils.repeatSound(100, 10, warningSound)
+ val currentTime = Instant.now()
+ nextWarningTime = currentTime.plusMillis(100)
+ finalWarningTime = finalWarningTime ?: currentTime.plusMillis(callLength.inWholeMilliseconds)
+ }
+
+ private fun stopWarningUser() {
+ activeWarning = false
+ finalWarningTime = null
+ nextWarningTime = null
+ }
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt
index 925eaadd84aa..a06b3dcbc202 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityCollectionStats.kt
@@ -9,6 +9,7 @@ import at.hannibal2.skyhanni.events.InventoryFullyOpenedEvent
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.CollectionUtils.addString
import at.hannibal2.skyhanni.utils.CollectionUtils.collectWhile
import at.hannibal2.skyhanni.utils.CollectionUtils.consumeWhile
import at.hannibal2.skyhanni.utils.DisplayTableEntry
@@ -39,9 +40,15 @@ object HoppityCollectionStats {
private val config get() = ChocolateFactoryAPI.config
private val patternGroup = ChocolateFactoryAPI.patternGroup.group("collection")
+
+ /**
+ * REGEX-TEST: (1/17) Hoppity's Collection
+ * REGEX-TEST: (12/17) Hoppity's Collection
+ * REGEX-TEST: Hoppity's Collection
+ */
private val pagePattern by patternGroup.pattern(
"page.current",
- "\\((?\\d+)/(?\\d+)\\) Hoppity's Collection",
+ "(?:\\((?\\d+)\\/(?\\d+)\\) )?Hoppity's Collection",
)
private val duplicatesFoundPattern by patternGroup.pattern(
"duplicates.found",
@@ -128,11 +135,13 @@ object HoppityCollectionStats {
@SubscribeEvent
fun onInventoryOpen(event: InventoryFullyOpenedEvent) {
- if (!isEnabled()) return
+ if (!(LorenzUtils.inSkyBlock)) return
if (!pagePattern.matches(event.inventoryName)) return
inInventory = true
- display = buildDisplay(event)
+ if (config.hoppityCollectionStats) {
+ display = buildDisplay(event)
+ }
}
@SubscribeEvent
@@ -144,6 +153,7 @@ object HoppityCollectionStats {
@SubscribeEvent
fun onBackgroundDraw(event: GuiRenderEvent.ChestGuiOverlayRenderEvent) {
if (!inInventory) return
+ if (!config.hoppityCollectionStats) return
config.hoppityStatsPosition.renderRenderables(
display,
@@ -211,7 +221,7 @@ object HoppityCollectionStats {
val foundRabbitCount = getFoundRabbitsFromHypixel(event)
if (loggedRabbitCount < foundRabbitCount) {
- newList.add(Renderable.string(""))
+ newList.addString("")
newList.add(
Renderable.wrappedString(
"§cPlease Scroll through \n" + "§call pages!",
@@ -361,8 +371,6 @@ object HoppityCollectionStats {
fun hasFoundRabbit(rabbit: String): Boolean = loggedRabbits.containsKey(rabbit)
- private fun isEnabled() = LorenzUtils.inSkyBlock && config.hoppityCollectionStats
-
enum class RabbitCollectionRarity(
val displayName: String,
val item: NEUInternalName,
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggDisplayManager.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggDisplayManager.kt
index f09aa4eac5da..fe908e58f62d 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggDisplayManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggDisplayManager.kt
@@ -76,7 +76,7 @@ object HoppityEggDisplayManager {
if (ReminderUtils.isBusy() && !config.showWhileBusy) return emptyList()
val displayList =
- HoppityEggType.entries.map { "§7 - ${it.formattedName} ${it.timeUntil().format()}" }.toMutableList()
+ HoppityEggType.resettingEntries.map { "§7 - ${it.formattedName} ${it.timeUntil().format()}" }.toMutableList()
displayList.add(0, "§bUnclaimed Eggs:")
if (config.showCollectedLocationCount && LorenzUtils.inSkyBlock) {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt
index dcf56aac6bbf..d15d43d8109e 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggLocator.kt
@@ -9,7 +9,6 @@ import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.ReceiveParticleEvent
import at.hannibal2.skyhanni.features.fame.ReminderUtils
import at.hannibal2.skyhanni.features.garden.GardenAPI
-import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
import at.hannibal2.skyhanni.utils.InventoryUtils
@@ -278,7 +277,7 @@ object HoppityEggLocator {
type == EnumParticleTypes.ENCHANTMENT_TABLE && speed == -2.0f && count == 10
fun isEnabled() = LorenzUtils.inSkyBlock && config.waypoints && !GardenAPI.inGarden() &&
- !ReminderUtils.isBusy(true) && ChocolateFactoryAPI.isHoppityEvent()
+ !ReminderUtils.isBusy(true) && HoppityAPI.isHoppityEvent()
private val ItemStack.isLocatorItem get() = getInternalName() == locatorItem
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt
index 2846262e6153..23e00cf88cdb 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggType.kt
@@ -14,9 +14,14 @@ enum class HoppityEggType(
BREAKFAST("Breakfast", "§6", 7),
LUNCH("Lunch", "§9", 14),
DINNER("Dinner", "§a", 21),
+ SIDE_DISH("Side Dish", "§6§l", -1),
+ BOUGHT("Bought", "§a", -1),
+ CHOCOLATE_SHOP_MILESTONE("Shop Milestone", "§6", -1),
+ CHOCOLATE_FACTORY_MILESTONE("Chocolate Milestone", "§6", -1)
;
fun timeUntil(): Duration {
+ if (resetsAt == -1) return Duration.INFINITE
val now = SkyBlockTime.now()
if (now.hour >= resetsAt) {
return now.copy(day = now.day + 1, hour = resetsAt, minute = 0, second = 0)
@@ -38,7 +43,9 @@ enum class HoppityEggType(
val coloredName get() = "$mealColor$mealName"
companion object {
- fun allFound() = entries.forEach { it.markClaimed() }
+ val resettingEntries = entries.filter { it.resetsAt != -1 }
+
+ fun allFound() = resettingEntries.forEach { it.markClaimed() }
fun getMealByName(mealName: String) = entries.find { it.mealName == mealName }
@@ -47,7 +54,7 @@ enum class HoppityEggType(
val currentSbDay = currentSbTime.day
val currentSbHour = currentSbTime.hour
- for (eggType in entries) {
+ for (eggType in resettingEntries) {
if (currentSbHour < eggType.resetsAt || eggType.lastResetDay == currentSbDay) continue
eggType.markSpawned()
eggType.lastResetDay = currentSbDay
@@ -60,11 +67,11 @@ enum class HoppityEggType(
}
fun eggsRemaining(): Boolean {
- return entries.any { !it.claimed }
+ return resettingEntries.any { !it.claimed }
}
fun allEggsRemaining(): Boolean {
- return entries.all { !it.claimed }
+ return resettingEntries.all { !it.claimed }
}
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt
index f9a25f3d596b..6bc1c8dfc276 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsCompactChat.kt
@@ -1,6 +1,11 @@
package at.hannibal2.skyhanni.features.event.hoppity
+import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEggsConfig
import at.hannibal2.skyhanni.events.LorenzChatEvent
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_FACTORY_MILESTONE
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.CHOCOLATE_SHOP_MILESTONE
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggType.SIDE_DISH
+import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.eggFoundPatterns
import at.hannibal2.skyhanni.features.event.hoppity.HoppityEggsManager.getEggType
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
import at.hannibal2.skyhanni.utils.ChatUtils
@@ -13,6 +18,8 @@ import at.hannibal2.skyhanni.utils.TimeUtils.format
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
+typealias RarityType = HoppityEggsConfig.CompactRarityTypes
+
object HoppityEggsCompactChat {
private var hoppityEggChat = mutableListOf()
@@ -64,26 +71,34 @@ object HoppityEggsCompactChat {
private fun createCompactMessage(): String {
val mealName = lastChatMeal?.coloredName ?: ""
- val mealNameFormatted = if (rabbitBought) "§aBought Rabbit" else "$mealName Egg"
+ val mealNameFormatted = if (rabbitBought) "§aBought Rabbit"
+ else if (lastChatMeal == SIDE_DISH) "§6§lSide Dish §r§6Egg"
+ else if (lastChatMeal == CHOCOLATE_SHOP_MILESTONE || lastChatMeal == CHOCOLATE_FACTORY_MILESTONE) "§6§lMilestone Rabbit"
+ else "$mealName Egg"
+ val rarityConfig = HoppityEggsManager.config.rarityInCompact
return if (duplicate) {
val format = lastDuplicateAmount?.shortFormat() ?: "?"
val timeFormatted = lastDuplicateAmount?.let {
ChocolateFactoryAPI.timeUntilNeed(it).format(maxUnits = 2)
} ?: "?"
+ val showDupeRarity = rarityConfig.let { it == RarityType.BOTH || it == RarityType.DUPE }
val timeStr = if (config.showDuplicateTime) ", §a+§b$timeFormatted§7" else ""
- "$mealNameFormatted! §7Duplicate $lastName §7(§6+$format Chocolate§7$timeStr)"
+ "$mealNameFormatted! §7Duplicate ${if (showDupeRarity) "$lastRarity " else ""}$lastName §7(§6+$format Chocolate§7$timeStr)"
} else if (newRabbit) {
- "$mealNameFormatted! §d§lNEW $lastName §7(${lastProfit}§7)"
+ val showNewRarity = rarityConfig.let { it == RarityType.BOTH || it == RarityType.NEW }
+ "$mealNameFormatted! §d§lNEW ${if (showNewRarity) "$lastRarity " else ""}$lastName §7(${lastProfit}§7)"
} else "?"
}
fun handleChat(event: LorenzChatEvent) {
- HoppityEggsManager.eggFoundPattern.matchMatcher(event.message) {
- resetCompactData()
- lastChatMeal = getEggType(event)
- compactChat(event)
+ eggFoundPatterns.forEach {
+ it.matchMatcher(event.message) {
+ resetCompactData()
+ lastChatMeal = getEggType(event)
+ compactChat(event)
+ }
}
HoppityEggsManager.eggBoughtPattern.matchMatcher(event.message) {
@@ -94,12 +109,12 @@ object HoppityEggsCompactChat {
}
HoppityEggsManager.rabbitFoundPattern.matchMatcher(event.message) {
- // The only case where "You found ..." will come in with more than 1 message,
- // or empty for hoppityEggChat, is where the rabbit was purchased from hoppity
- // in this case, we want to reset variables to a clean state during this capture,
+ // The only cases where "You found ..." will come in with more than 1 message,
+ // or empty for hoppityEggChat, is where the rabbit was purchased from hoppity,
+ // In the case of buying, we want to reset variables to a clean state during this capture,
// as the important capture for the purchased message is the final message in
// the chain; "You found [rabbit]" -> "Dupe/New Rabbit" -> "You bought [rabbit]"
- if (hoppityEggChat.isEmpty() || hoppityEggChat.size > 1) {
+ if ((hoppityEggChat.isEmpty() || hoppityEggChat.size > 1)) {
resetCompactData()
}
@@ -124,7 +139,9 @@ object HoppityEggsCompactChat {
}
}
- fun clickableCompact(onClick: () -> Unit): Boolean = if (hoppityEggChat.isNotEmpty()) {
+ fun clickableCompact(onClick: () -> Unit): Boolean = if (hoppityEggChat.isNotEmpty() && !rabbitBought && lastChatMeal != null &&
+ HoppityEggType.resettingEntries.contains(lastChatMeal)
+ ) {
val hover = hoppityEggChat.joinToString("\n") + " \n§eClick here to share the location of this chocolate egg with the server!"
hoppityEggChat.clear()
ChatUtils.clickableChat(
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt
index 93ae797e7581..bec966128dd3 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEggsManager.kt
@@ -25,6 +25,7 @@ import at.hannibal2.skyhanni.utils.StringUtils.removeColor
import at.hannibal2.skyhanni.utils.TimeUtils.format
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
import java.util.regex.Matcher
+import java.util.regex.Pattern
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
@@ -37,12 +38,36 @@ object HoppityEggsManager {
* REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§9Chocolate Lunch Egg §r§don a ledge next to the stairs up§r§d!
* REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§aChocolate Dinner Egg §r§dbehind Emissary Sisko§r§d!
* REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§9Chocolate Lunch Egg §r§dnear the Diamond Essence Shop§r§d!
+ * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d!
*/
- val eggFoundPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ private val eggFoundPattern by ChocolateFactoryAPI.patternGroup.pattern(
"egg.found",
"§d§lHOPPITY'S HUNT §r§dYou found a §r§.Chocolate (?\\w+) Egg §r§d(?.*)§r§d!",
)
+ /**
+ * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou found a §r§6§lSide Dish §r§6Egg §r§din the Chocolate Factory§r§d!
+ */
+ private val sideDishEggFoundPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "sidedish.found",
+ "§d§lHOPPITY'S HUNT §r§dYou found a §r§6§l(?.*) §r§6Egg §r§din the Chocolate Factory§r§d!",
+ )
+
+ /**
+ * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lShop Milestone Rabbit §r§din the Chocolate Factory§r§d!
+ * REGEX-TEST: §d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§lChocolate Milestone Rabbit §r§din the Chocolate Factory§r§d!
+ */
+ private val milestoneRabbitFoundPattern by ChocolateFactoryAPI.patternGroup.pattern(
+ "milestone.claimed",
+ "§d§lHOPPITY'S HUNT §r§dYou claimed a §r§6§l(?[\\w ]+) Rabbit §r§din the Chocolate Factory§r§d!",
+ )
+
+ val eggFoundPatterns: List = listOf(
+ eggFoundPattern,
+ sideDishEggFoundPattern,
+ milestoneRabbitFoundPattern,
+ )
+
/**
* REGEX-TEST: §aYou bought §r§9Casanova §r§afor §r§6970,000 Coins§r§a!
* REGEX-TEST: §aYou bought §r§fHeidie §r§afor §r§6194,000 Coins§r§a!
@@ -74,7 +99,7 @@ object HoppityEggsManager {
val duplicateRabbitFound by ChocolateFactoryAPI.patternGroup.pattern(
"rabbit.duplicate",
- "§7§lDUPLICATE RABBIT! §6\\+(?[\\d,]+) Chocolate"
+ "§7§lDUPLICATE RABBIT! §6\\+(?[\\d,]+) Chocolate",
)
private val noEggsLeftPattern by ChocolateFactoryAPI.patternGroup.pattern(
@@ -123,8 +148,9 @@ object HoppityEggsManager {
}
HoppityEggsCompactChat.handleChat(event)
+ HoppityAPI.handleChat(event)
- if (!ChocolateFactoryAPI.isHoppityEvent()) return
+ if (!HoppityAPI.isHoppityEvent()) return
eggFoundPattern.matchMatcher(event.message) {
HoppityEggLocations.saveNearestEgg()
@@ -141,7 +167,7 @@ object HoppityEggsManager {
HoppityEggType.allFound()
if (config.timeInChat) {
- val nextEgg = HoppityEggType.entries.minByOrNull { it.timeUntil() } ?: return
+ val nextEgg = HoppityEggType.resettingEntries.minByOrNull { it.timeUntil() } ?: return
ChatUtils.chat("§eNext egg available in §b${nextEgg.timeUntil().format()}§e.")
event.blockedReason = "hoppity_egg"
}
@@ -151,7 +177,7 @@ object HoppityEggsManager {
eggAlreadyCollectedPattern.matchMatcher(event.message) {
getEggType(event).markClaimed()
if (config.timeInChat) {
- val nextEgg = HoppityEggType.entries.minByOrNull { it.timeUntil() } ?: return
+ val nextEgg = HoppityEggType.resettingEntries.minByOrNull { it.timeUntil() } ?: return
ChatUtils.chat("§eNext egg available in §b${nextEgg.timeUntil().format()}§e.")
event.blockedReason = "hoppity_egg"
}
@@ -222,22 +248,20 @@ object HoppityEggsManager {
if (lastWarnTime.passedSince() < 1.minutes) return
lastWarnTime = now()
- val amount = HoppityEggType.entries.size
+ val amount = HoppityEggType.resettingEntries.size
val message = "All $amount Hoppity Eggs are ready to be found!"
if (config.warpUnclaimedEggs) {
- if (LorenzUtils.inSkyBlock) {
- ChatUtils.clickableChat(
- message,
- onClick = { HypixelCommands.warp(config.warpDestination) },
- "§eClick to ${"/warp ${config.warpDestination}".trim()}!",
- )
+ val (action, actionName) = if (LorenzUtils.inSkyBlock) {
+ { HypixelCommands.warp(config.warpDestination) } to "${"warp to ${config.warpDestination}".trim()}"
} else {
- ChatUtils.clickableChat(
- message,
- onClick = { HypixelCommands.skyblock() },
- "§eClick to join /skyblock!",
- )
+ { HypixelCommands.skyblock() } to "join /skyblock!"
}
+ ChatUtils.clickToActionOrDisable(
+ message,
+ config::warpUnclaimedEggs,
+ actionName = actionName,
+ action = action,
+ )
} else ChatUtils.chat(message)
LorenzUtils.sendTitle("§e$amount Hoppity Eggs!", 5.seconds)
SoundUtils.repeatSound(100, 10, SoundUtils.plingSound)
@@ -255,5 +279,5 @@ object HoppityEggsManager {
}
fun isActive() = (LorenzUtils.inSkyBlock || (LorenzUtils.onHypixel && config.showOutsideSkyblock)) &&
- ChocolateFactoryAPI.isHoppityEvent()
+ HoppityAPI.isHoppityEvent()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEventSummary.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEventSummary.kt
new file mode 100644
index 000000000000..0d4e09b9c518
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityEventSummary.kt
@@ -0,0 +1,277 @@
+package at.hannibal2.skyhanni.features.event.hoppity
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.api.event.HandleEvent
+import at.hannibal2.skyhanni.config.features.event.hoppity.HoppityEventSummaryConfig.HoppityStat
+import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage.HoppityEventStats
+import at.hannibal2.skyhanni.config.storage.ProfileSpecificStorage.HoppityEventStats.RabbitData
+import at.hannibal2.skyhanni.data.ProfileStorageData
+import at.hannibal2.skyhanni.events.ProfileJoinEvent
+import at.hannibal2.skyhanni.events.SecondPassedEvent
+import at.hannibal2.skyhanni.events.hoppity.RabbitFoundEvent
+import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.sumAllValues
+import at.hannibal2.skyhanni.utils.LorenzRarity
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
+import at.hannibal2.skyhanni.utils.SkyBlockTime
+import at.hannibal2.skyhanni.utils.SkyBlockTime.Companion.SKYBLOCK_DAY_MILLIS
+import at.hannibal2.skyhanni.utils.SkyBlockTime.Companion.SKYBLOCK_HOUR_MILLIS
+import at.hannibal2.skyhanni.utils.SkyblockSeason
+import at.hannibal2.skyhanni.utils.StringUtils
+import at.hannibal2.skyhanni.utils.TimeUtils.format
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.milliseconds
+
+@SkyHanniModule
+object HoppityEventSummary {
+ private val config get() = SkyHanniMod.feature.event.hoppityEggs
+ private val lineHeader = " ".repeat(4)
+
+ private var firstInCf = 0L
+ private var inCfNow = false
+
+ @HandleEvent
+ fun onRabbitFound(event: RabbitFoundEvent) {
+ if (!HoppityAPI.isHoppityEvent()) return
+ val stats = getYearStats().first ?: return
+
+ stats.mealsFound.addOrPut(event.eggType, 1)
+ val rarity = HoppityAPI.rarityByRabbit(event.rabbitName) ?: return
+ val rarityMap = stats.rabbitsFound.getOrPut(rarity) { RabbitData() }
+ if (event.duplicate) rarityMap.dupes++
+ else rarityMap.uniques++
+ if (event.chocGained > 0) stats.dupeChocolateGained += event.chocGained
+ }
+
+ @SubscribeEvent
+ fun onSecondPassed(event: SecondPassedEvent) {
+ if (!LorenzUtils.inSkyBlock) return
+ checkEnded()
+ if (!HoppityAPI.isHoppityEvent()) return
+ checkInit()
+ checkAddCfTime()
+ }
+
+ @SubscribeEvent
+ fun onProfileJoin(event: ProfileJoinEvent) {
+ checkEnded()
+ }
+
+ private fun checkInit() {
+ val statStorage = ProfileStorageData.profileSpecific?.hoppityEventStats ?: return
+ val currentYear = SkyBlockTime.now().year
+ if (statStorage.containsKey(currentYear)) return
+ statStorage[currentYear] = HoppityEventStats()
+ }
+
+ private fun getYearStats(year: Int? = null): Pair {
+ val queryYear = year ?: SkyBlockTime.now().year
+ val storage = ProfileStorageData.profileSpecific?.hoppityEventStats ?: return Pair(null, queryYear)
+ if (!storage.containsKey(queryYear)) return Pair(null, queryYear)
+ return Pair(storage[queryYear], queryYear)
+ }
+
+ private fun checkAddCfTime() {
+ if (ChocolateFactoryAPI.inChocolateFactory && !this.inCfNow) {
+ this.inCfNow = true
+ firstInCf = SkyBlockTime.now().toMillis()
+ } else if (!ChocolateFactoryAPI.inChocolateFactory && this.inCfNow) {
+ val stats = getYearStats().first ?: return
+ stats.millisInCf += (SkyBlockTime.now().toMillis() - firstInCf)
+ this.inCfNow = false
+ firstInCf = 0
+ }
+ }
+
+ private fun checkEnded() {
+ val (stats, year) = getYearStats()
+ if (stats == null || stats.summarized) return
+
+ val currentYear = SkyBlockTime.now().year
+ val currentSeason = SkyblockSeason.currentSeason
+ val isSpring = currentSeason == SkyblockSeason.SPRING
+
+ if (year < currentYear || (year == currentYear && !isSpring) && config.eventSummary.enabled) {
+ sendStatsMessage(stats, year)
+ (ProfileStorageData.profileSpecific?.hoppityEventStats?.get(year)?.also { it.summarized = true }
+ ?: ErrorManager.skyHanniError("Could not save summarization state in Hoppity Event Summarization."))
+ }
+ }
+
+ // First event was year 346 -> #1, 20th event was year 365, etc.
+ private fun getHoppityEventNumber(skyblockYear: Int): Int = (skyblockYear - 345)
+
+ fun addStrayCaught(rarity: LorenzRarity, chocGained: Long) {
+ if (!HoppityAPI.isHoppityEvent()) return
+ val stats = getYearStats().first ?: return
+ val rarityMap = stats.rabbitsFound.getOrPut(rarity) { RabbitData() }
+ rarityMap.strays++
+ stats.strayChocolateGained += chocGained
+ }
+
+ private fun StringBuilder.appendHeadedLine(line: String) {
+ appendLine("$lineHeader$line")
+ }
+
+ private fun StringBuilder.addExtraChocFormatLine(chocGained: Long) {
+ if (chocGained <= 0) return
+ appendHeadedLine(
+ buildString {
+ append(" §6+${chocGained.addSeparators()} Chocolate")
+ if (SkyHanniMod.feature.inventory.chocolateFactory.showDuplicateTime) {
+ val timeFormatted = ChocolateFactoryAPI.timeUntilNeed(chocGained).format(maxUnits = 2)
+ append(" §7(§a+§b$timeFormatted§7)")
+ }
+ },
+ )
+ }
+
+ private val summaryOperationList by lazy {
+ buildMap Unit> {
+ put(HoppityStat.MEAL_EGGS_FOUND) { sb, stats, year ->
+ stats.getEggsFoundFormat(year).takeIf { it != null }?.let {
+ sb.appendHeadedLine(it)
+ }
+ }
+
+ put(HoppityStat.HOPPITY_RABBITS_BOUGHT) { sb, stats, _ ->
+ stats.mealsFound[HoppityEggType.BOUGHT]?.let {
+ sb.appendHeadedLine("§7You bought §b$it §f${StringUtils.pluralize(it, "Rabbit")} §7from §aHoppity§7.")
+ }
+ }
+
+ put(HoppityStat.SIDE_DISH_EGGS) { sb, stats, _ ->
+ stats.mealsFound[HoppityEggType.SIDE_DISH]?.let {
+ sb.appendHeadedLine(
+ "§7You found §b$it §6§lSide Dish §r§6${StringUtils.pluralize(it, "Egg")}§7 " +
+ "§7in the §6Chocolate Factory§7.",
+ )
+ }
+ }
+
+ put(HoppityStat.MILESTONE_RABBITS) { sb, stats, _ ->
+ ((stats.mealsFound[HoppityEggType.CHOCOLATE_FACTORY_MILESTONE] ?: 0) +
+ (stats.mealsFound[HoppityEggType.CHOCOLATE_SHOP_MILESTONE] ?: 0)).takeIf { it > 0 }?.let {
+ sb.appendHeadedLine("§7You claimed §b$it §6§lMilestone §r§6${StringUtils.pluralize(it, "Rabbit")}§7.")
+ }
+ }
+
+ put(HoppityStat.NEW_RABBITS) { sb, stats, _ ->
+ getRabbitsFormat(stats.rabbitsFound.mapValues { m -> m.value.uniques }, "Unique").forEach {
+ sb.appendHeadedLine(it)
+ }
+ }
+
+ put(HoppityStat.DUPLICATE_RABBITS) { sb, stats, _ ->
+ getRabbitsFormat(stats.rabbitsFound.mapValues { m -> m.value.dupes }, "Duplicate").forEach {
+ sb.appendHeadedLine(it)
+ }
+ sb.addExtraChocFormatLine(stats.dupeChocolateGained)
+ }
+
+ put(HoppityStat.STRAY_RABBITS) { sb, stats, _ ->
+ getRabbitsFormat(stats.rabbitsFound.mapValues { m -> m.value.strays }, "Stray").forEach {
+ sb.appendHeadedLine(it)
+ }
+ sb.addExtraChocFormatLine(stats.strayChocolateGained)
+ }
+
+ put(HoppityStat.TIME_IN_CF) { sb, stats, _ ->
+ sb.appendHeadedLine("§7You spent §b${stats.millisInCf.milliseconds.format(maxUnits = 2)} §7in the §6Chocolate Factory§7.")
+ }
+
+ put(HoppityStat.EMPTY_1) { sb, _, _ -> sb.appendLine() }
+ put(HoppityStat.EMPTY_2) { sb, _, _ -> sb.appendLine() }
+ put(HoppityStat.EMPTY_3) { sb, _, _ -> sb.appendLine() }
+ put(HoppityStat.EMPTY_4) { sb, _, _ -> sb.appendLine() }
+ }
+ }
+
+ fun sendStatsMessage(it: Array) {
+ val statsStorage = ProfileStorageData.profileSpecific?.hoppityEventStats ?: return
+ val currentYear = SkyBlockTime.now().year
+ val statsYearList = statsStorage.keys.takeIf { it.isNotEmpty() } ?: mutableListOf()
+ val statsYearFormatList = statsStorage.keys.takeIf { it.isNotEmpty() }?.map {
+ "§b$it${if (it == currentYear && HoppityAPI.isHoppityEvent()) " §a(Current)§r" else ""}"
+ }?.toMutableList() ?: mutableListOf()
+
+ val parsedInt: Int? = if (it.size == 1) it[0].toIntOrNull() else null
+
+ val availableYearsFormat = "§eHoppity Event Stats are available for the following years:§r\n${statsYearFormatList.joinToString("§e, ") { it }}"
+
+ if (parsedInt == null) {
+ if (HoppityAPI.isHoppityEvent()) {
+ val stats = getYearStats(currentYear).first ?: return
+ sendStatsMessage(stats, currentYear)
+ } else ChatUtils.chat(availableYearsFormat)
+ } else if (!statsYearList.contains(parsedInt)) {
+ ChatUtils.chat("Could not find data for year §b$parsedInt§e.\n$availableYearsFormat")
+ } else {
+ val stats = getYearStats(parsedInt).first ?: return
+ sendStatsMessage(stats, parsedInt)
+ }
+ }
+
+ private fun sendStatsMessage(stats: HoppityEventStats, eventYear: Int?) {
+ if (eventYear == null) return
+ val summaryBuilder: StringBuilder = StringBuilder()
+ summaryBuilder.appendLine("§d§l${"▬".repeat(64)}")
+
+ // Header
+ summaryBuilder.appendLine("${" ".repeat(26)}§d§lHoppity's Hunt #${getHoppityEventNumber(eventYear)} Stats")
+ summaryBuilder.appendLine()
+
+ // Various stats from config
+ val statsBuilder: StringBuilder = StringBuilder()
+ config.eventSummary.statDisplayList.forEach {
+ summaryOperationList[it]?.invoke(statsBuilder, stats, eventYear)
+ }
+ if (statsBuilder.toString().replace("\n", "").isEmpty()) {
+ statsBuilder.appendHeadedLine("§c§lNothing to show!\n§c§oGo find some eggs!")
+ }
+
+ // Append stats
+ summaryBuilder.append(statsBuilder)
+
+ // Footer
+ summaryBuilder.append("§d§l${"▬".repeat(64)}")
+
+ ChatUtils.chat(summaryBuilder.toString(), prefix = false)
+ }
+
+ private fun HoppityEventStats.getEggsFoundFormat(year: Int): String? =
+ mealsFound.filterKeys { it in HoppityEggType.resettingEntries }.sumAllValues().toInt().takeIf { it > 0 }?.let {
+ val milliDifference = SkyBlockTime.now().toMillis() - SkyBlockTime.fromSbYear(year).toMillis()
+ val pastEvent = milliDifference > SkyBlockTime.SKYBLOCK_SEASON_MILLIS
+ // Calculate total eggs from complete days and incomplete day periods
+ val previousEggs = if (pastEvent) 279 else (milliDifference / SKYBLOCK_DAY_MILLIS).toInt() * 3
+ val currentEggs = when {
+ pastEvent -> 0
+ // Add eggs for the current day based on time of day
+ milliDifference % SKYBLOCK_DAY_MILLIS >= SKYBLOCK_HOUR_MILLIS * 21 -> 3 // Dinner egg, 9 PM
+ milliDifference % SKYBLOCK_DAY_MILLIS >= SKYBLOCK_HOUR_MILLIS * 14 -> 2 // Lunch egg, 2 PM
+ milliDifference % SKYBLOCK_DAY_MILLIS >= SKYBLOCK_HOUR_MILLIS * 7 -> 1 // Breakfast egg, 7 AM
+ else -> 0
+ }
+ val spawnedMealsEggs = previousEggs + currentEggs
+ "§7You found §b$it§7/§a$spawnedMealsEggs §6Chocolate Meal ${StringUtils.pluralize(it, "Egg")}§7."
+ }
+
+
+ private fun getRabbitsFormat(rarityMap: Map, name: String): List {
+ val rabbitsSum = rarityMap.values.sum()
+ if (rabbitsSum == 0) return emptyList()
+
+ return mutableListOf(
+ "§7$name Rabbits: §f$rabbitsSum",
+ HoppityAPI.hoppityRarities.joinToString(" §7-") {
+ " ${it.chatColorCode}${rarityMap[it] ?: 0}"
+ },
+ )
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityNpc.kt b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityNpc.kt
index 877e161ef64f..70e148aa9732 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityNpc.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/hoppity/HoppityNpc.kt
@@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.features.fame.ReminderUtils
import at.hannibal2.skyhanni.features.inventory.chocolatefactory.ChocolateFactoryAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.LorenzColor
@@ -50,16 +51,14 @@ object HoppityNpc {
if (!isReminderEnabled()) return
if (ReminderUtils.isBusy()) return
if (hoppityYearOpened == SkyBlockTime.now().year) return
- if (!ChocolateFactoryAPI.isHoppityEvent()) return
+ if (!HoppityAPI.isHoppityEvent()) return
if (lastReminderSent.passedSince() <= 2.minutes) return
- ChatUtils.clickableChat(
- "New rabbits are available at §aHoppity's Shop§e! §c(Click to disable this reminder)",
- onClick = {
- disableReminder()
- ChatUtils.chat("§eHoppity's Shop reminder disabled.")
- },
- oneTimeClick = true
+ ChatUtils.clickToActionOrDisable(
+ "New rabbits are available at §aHoppity's Shop§e!",
+ config::hoppityShopReminder,
+ actionName = "warp to hub",
+ action = { HypixelCommands.warp("hub") },
)
lastReminderSent = SimpleTimeMark.now()
@@ -104,8 +103,4 @@ object HoppityNpc {
inShop = false
slotsToHighlight.clear()
}
-
- private fun disableReminder() {
- config.hoppityShopReminder = false
- }
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/jerry/frozentreasure/FrozenTreasureTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/event/jerry/frozentreasure/FrozenTreasureTracker.kt
index 7547f1bc2f83..80812c4c563b 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/jerry/frozentreasure/FrozenTreasureTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/jerry/frozentreasure/FrozenTreasureTracker.kt
@@ -11,8 +11,8 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.ConfigUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
@@ -20,6 +20,7 @@ import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
+import at.hannibal2.skyhanni.utils.renderables.Searchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
import at.hannibal2.skyhanni.utils.tracker.TrackerData
@@ -104,8 +105,8 @@ object FrozenTreasureTracker {
icePerHour = (icePerSecond.average() * 3600).toInt()
}
- private fun formatDisplay(map: List>): List> {
- val newList = mutableListOf>()
+ private fun formatDisplay(map: List): List {
+ val newList = mutableListOf()
for (index in config.textFormat) {
// TODO, change functionality to use enum rather than ordinals
newList.add(map[index.ordinal])
@@ -136,20 +137,20 @@ object FrozenTreasureTracker {
}
}
- private fun drawDisplay(data: Data) = buildList> {
+ private fun drawDisplay(data: Data) = buildList {
calculateIce(data)
- addAsSingletonList("§e§lFrozen Treasure Tracker")
- addAsSingletonList("§6${formatNumber(data.treasuresMined)} Treasures Mined")
- addAsSingletonList("§3${formatNumber(estimatedIce)} Total Ice")
- addAsSingletonList("§3${formatNumber(icePerHour)} Ice/hr")
- addAsSingletonList("§8${formatNumber(data.compactProcs)} Compact Procs")
- addAsSingletonList("")
+ addSearchString("§e§lFrozen Treasure Tracker")
+ addSearchString("§6${formatNumber(data.treasuresMined)} Treasures Mined")
+ addSearchString("§3${formatNumber(estimatedIce)} Total Ice")
+ addSearchString("§3${formatNumber(icePerHour)} Ice/hr")
+ addSearchString("§8${formatNumber(data.compactProcs)} Compact Procs")
+ addSearchString("")
for (treasure in FrozenTreasure.entries) {
val count = (data.treasureCount[treasure] ?: 0) * if (config.showAsDrops) treasure.defaultAmount else 1
- addAsSingletonList("§b${formatNumber(count)} ${treasure.displayName}")
+ addSearchString("§b${formatNumber(count)} ${treasure.displayName}", treasure.displayName)
}
- addAsSingletonList("")
+ addSearchString("")
}
fun formatNumber(amount: Number): String {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/lobby/waypoints/christmas/PresentWaypoints.kt b/src/main/java/at/hannibal2/skyhanni/features/event/lobby/waypoints/christmas/PresentWaypoints.kt
index 64c4171a31b0..be54088714ea 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/lobby/waypoints/christmas/PresentWaypoints.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/lobby/waypoints/christmas/PresentWaypoints.kt
@@ -37,15 +37,15 @@ object PresentWaypoints {
private val patternGroup = RepoPattern.group("event.lobby.waypoint.presents")
private val presentAlreadyFoundPattern by patternGroup.pattern(
"foundalready",
- "§cYou have already found this present!"
+ "§cYou have already found this present!",
)
private val presentFoundPattern by patternGroup.pattern(
"found",
- "§aYou found a.*present! §r§e\\(§r§b\\d+§r§e/§r§b\\d+§r§e\\)"
+ "§aYou found a.*present! §r§e\\(§r§b\\d+§r§e/§r§b\\d+§r§e\\)",
)
private val allFoundPattern by patternGroup.pattern(
"foundall",
- "§aCongratulations! You found all the presents in every lobby!"
+ "§aCongratulations! You found all the presents in every lobby!",
)
@SubscribeEvent
@@ -125,5 +125,5 @@ object PresentWaypoints {
}
private fun isEnabled(): Boolean =
- LorenzUtils.inHypixelLobby && (config.allWaypoints || config.allEntranceWaypoints && WinterAPI.isDecember())
+ LorenzUtils.inHypixelLobby && (config.allWaypoints || config.allEntranceWaypoints) && WinterAPI.isDecember()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/event/winter/NewYearCakeReminder.kt b/src/main/java/at/hannibal2/skyhanni/features/event/winter/NewYearCakeReminder.kt
index f64204536957..785c2c358dcc 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/event/winter/NewYearCakeReminder.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/event/winter/NewYearCakeReminder.kt
@@ -24,7 +24,7 @@ object NewYearCakeReminder {
private val config get() = SkyHanniMod.feature.event.winter
private val sidebarDetectionPattern by RepoPattern.pattern(
"event.winter.newyearcake.reminder.sidebar",
- "§dNew Year Event!§f (?.*)"
+ "§dNew Year Event!§f (?.*)",
)
private var lastReminderSend = SimpleTimeMark.farPast()
@@ -63,11 +63,11 @@ object NewYearCakeReminder {
if (lastReminderSend.passedSince() < 30.seconds) return
lastReminderSend = SimpleTimeMark.now()
-
- ChatUtils.clickableChat(
+ ChatUtils.clickToActionOrDisable(
"Reminding you to grab the free New Year Cake. Click here to open the baker menu!",
- onClick = { HypixelCommands.openBaker() },
- "§eClick to run /openbaker!",
+ config::newYearCakeReminder,
+ actionName = "open the baker menu",
+ action = { HypixelCommands.openBaker() },
)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt b/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt
index 4457144021aa..5a1e1361a784 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fame/CityProjectFeatures.kt
@@ -12,6 +12,7 @@ import at.hannibal2.skyhanni.features.inventory.bazaar.BazaarApi
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.InventoryUtils.getUpperItems
import at.hannibal2.skyhanni.utils.ItemUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
@@ -53,18 +54,13 @@ object CityProjectFeatures {
private val patternGroup = RepoPattern.group("fame.projects")
private val contributeAgainPattern by patternGroup.pattern(
"contribute",
- "§7Contribute again: §e(?.*)"
+ "§7Contribute again: §e(?.*)",
)
private val completedPattern by patternGroup.pattern(
"completed",
- "§aProject is (?:being built|released)!"
+ "§aProject is (?:being built|released)!",
)
- fun disable() {
- config.dailyReminder = false
- ChatUtils.chat("Disabled city project reminder messages!")
- }
-
@SubscribeEvent
fun onSecondPassed(event: SecondPassedEvent) {
if (!config.dailyReminder) return
@@ -79,11 +75,11 @@ object CityProjectFeatures {
if (lastReminderSend.passedSince() < 30.seconds) return
lastReminderSend = SimpleTimeMark.now()
- ChatUtils.clickableChat(
- "Daily City Project Reminder! (Click here to disable this reminder)",
- onClick = { disable() },
- "§eClick to disable!",
- oneTimeClick = true
+ ChatUtils.clickToActionOrDisable(
+ "Daily City Project Reminder!",
+ config::dailyReminder,
+ actionName = "warp to Hub",
+ action = { HypixelCommands.warp("hub") },
)
}
@@ -166,8 +162,8 @@ object CityProjectFeatures {
} else {
BazaarApi.searchForBazaarItem(name, amount)
}
- }
- ) { inInventory && !NEUItems.neuHasFocus() }
+ },
+ ) { inInventory && !NEUItems.neuHasFocus() },
)
val price = internalName.getPrice() * amount
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fame/UpgradeReminder.kt b/src/main/java/at/hannibal2/skyhanni/features/fame/UpgradeReminder.kt
index 22171ed3bf53..d5f33204ce25 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fame/UpgradeReminder.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fame/UpgradeReminder.kt
@@ -10,6 +10,7 @@ import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
+import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.ItemUtils.getLore
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.RegexUtils.anyMatches
@@ -27,31 +28,33 @@ import kotlin.time.Duration.Companion.seconds
@SkyHanniModule
object UpgradeReminder {
+ private val config get() = SkyHanniMod.feature.misc
+
private val patternGroup = RepoPattern.group("fame.upgrades")
private val accountUpgradePattern by patternGroup.pattern(
"account",
- "§8Account Upgrade"
+ "§8Account Upgrade",
)
private val profileUpgradePattern by patternGroup.pattern(
"profile",
- "§8Profile Upgrade"
+ "§8Profile Upgrade",
)
private val upgradeDurationPattern by patternGroup.pattern(
"duration",
- "§8Duration: (?.+)"
+ "§8Duration: (?.+)",
)
private val upgradeStartedPattern by patternGroup.pattern(
"started",
- "§eYou started the §r§a(?.+) §r§eupgrade!"
+ "§eYou started the §r§a(?.+) §r§eupgrade!",
)
private val upgradeClaimedPattern by patternGroup.pattern(
"claimed",
- "§eYou claimed the §r§a(?.+) §r§eupgrade!"
+ "§eYou claimed the §r§a(?.+) §r§eupgrade!",
)
private val upgradePattern by patternGroup.pattern(
"upgrade",
- "§eClick to start upgrade!"
+ "§eClick to start upgrade!",
)
private var currentProfileUpgrade: CommunityShopUpgrade?
@@ -74,7 +77,6 @@ object UpgradeReminder {
// TODO: (for 0.27) merge this logic with reminder manager
@SubscribeEvent
fun onSecondPassed(event: SecondPassedEvent) {
- if (!LorenzUtils.inSkyBlock) return
if (!isEnabled()) return
if (ReminderUtils.isBusy()) return
if (inInventory || LorenzUtils.skyBlockArea == "Community Center") return
@@ -127,28 +129,26 @@ object UpgradeReminder {
}
}
- private fun isEnabled() = SkyHanniMod.feature.misc.accountUpgradeReminder
-
- fun disable() {
- SkyHanniMod.feature.misc.accountUpgradeReminder = false
- }
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.accountUpgradeReminder
@SubscribeEvent
fun onConfigFix(event: ConfigUpdaterMigrator.ConfigFixEvent) {
- event.move(49,
+ event.move(
+ 49,
"#player.currentAccountUpgrade",
- "#player.communityShopAccountUpgrade.name"
+ "#player.communityShopAccountUpgrade.name",
)
- event.move(49,
+ event.move(
+ 49,
"#player.nextAccountUpgradeCompletionTime",
- "#player.communityShopAccountUpgrade.completionTime"
+ "#player.communityShopAccountUpgrade.completionTime",
)
}
class CommunityShopUpgrade(
@Expose val name: String?,
- @Expose var completionTime: SimpleTimeMark = SimpleTimeMark.farFuture()
+ @Expose var completionTime: SimpleTimeMark = SimpleTimeMark.farFuture(),
) {
private var duration: Duration = Duration.ZERO
@@ -158,10 +158,11 @@ object UpgradeReminder {
fun sendReminderIfClaimable() {
if (this.name == null || this.completionTime.isInFuture()) return
- ChatUtils.clickableChat(
- "The §a$name §eupgrade has completed! §c(Click to disable these reminders)", onClick = {
- disable()
- }, oneTimeClick = true
+ ChatUtils.clickToActionOrDisable(
+ "The §a$name §eupgrade has completed!",
+ config::accountUpgradeReminder,
+ actionName = "warp to Hub",
+ action = { HypixelCommands.warp("hub") },
)
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt
index 74cf9294da64..5be021fbb8a1 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/FishingTimer.kt
@@ -125,7 +125,7 @@ object FishingTimer {
private fun handle() {
if (lastSeaCreatureFished.passedSince() > 2.seconds) return
val name = lastNameFished ?: return
- val mobs = recentMobs.filter { it.name == name && it !in mobDespawnTime }
+ val mobs = recentMobs.toSet().filter { it.name == name && it !in mobDespawnTime }
.sortedBy { it.baseEntity.distanceToPlayer() }
.take(mobsToFind).ifEmpty { return }
mobsToFind -= mobs.size
@@ -165,9 +165,7 @@ object FishingTimer {
}
private fun updateInfo() {
- currentCount = mobDespawnTime.entries.sumOf {
- 1 + it.key.extraEntities.size
- }
+ currentCount = mobDespawnTime.size
startTime = mobDespawnTime.maxByOrNull { it.value.passedSince() }?.value ?: SimpleTimeMark.farPast()
display = createDisplay()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/ShowFishingItemName.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/ShowFishingItemName.kt
index f50cd9a6bb13..04b4fc7489ce 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/ShowFishingItemName.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/ShowFishingItemName.kt
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.features.fishing
import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.features.fishing.FishingAPI.isBait
@@ -10,6 +11,7 @@ import at.hannibal2.skyhanni.utils.EntityUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getSkullTexture
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.LorenzUtils.isInIsland
import at.hannibal2.skyhanni.utils.RenderUtils.drawString
import at.hannibal2.skyhanni.utils.RenderUtils.exactLocation
import at.hannibal2.skyhanni.utils.StringUtils.removeColor
@@ -27,7 +29,7 @@ object ShowFishingItemName {
// Taken from Skytils
private val cheapCoins = setOf(
"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTM4MDcxNzIxY2M1YjRjZDQwNmNlNDMxYTEzZjg2MDgzYTg5NzNlMTA2NGQyZjg4OTc4Njk5MzBlZTZlNTIzNyJ9fX0=",
- "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGZhMDg3ZWI3NmU3Njg3YTgxZTRlZjgxYTdlNjc3MjY0OTk5MGY2MTY3Y2ViMGY3NTBhNGM1ZGViNmM0ZmJhZCJ9fX0="
+ "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGZhMDg3ZWI3NmU3Njg3YTgxZTRlZjgxYTdlNjc3MjY0OTk5MGY2MTY3Y2ViMGY3NTBhNGM1ZGViNmM0ZmJhZCJ9fX0=",
)
@SubscribeEvent
@@ -67,5 +69,18 @@ object ShowFishingItemName {
}
}
- fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled && FishingAPI.holdingRod
+ fun inCorrectArea(): Boolean {
+ if (IslandType.HUB.isInIsland()) {
+ LorenzUtils.skyBlockArea?.let {
+ if (it.endsWith(" Atrium")) return false
+ if (it.endsWith(" Museum")) return false
+ if (it == "Fashion Shop") return false
+ if (it == "Shen's Auction") return false
+ }
+ }
+ if (IslandType.THE_END.isInIsland()) return false
+ return true
+ }
+
+ fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled && FishingAPI.holdingRod && inCorrectArea()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/FishingProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/FishingProfitTracker.kt
index b99c03ff57a3..12f3accd1de1 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/FishingProfitTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/FishingProfitTracker.kt
@@ -1,6 +1,7 @@
package at.hannibal2.skyhanni.features.fishing.tracker
import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.ItemAddManager
import at.hannibal2.skyhanni.data.jsonobjects.repo.FishingProfitItemsJson
import at.hannibal2.skyhanni.events.FishingBobberCastEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
@@ -12,10 +13,9 @@ import at.hannibal2.skyhanni.features.fishing.FishingAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ChatUtils
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.DelayedRun
import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.LorenzUtils.addButton
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
@@ -25,6 +25,9 @@ import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils
import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.addButton
+import at.hannibal2.skyhanni.utils.renderables.Searchable
+import at.hannibal2.skyhanni.utils.renderables.toSearchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData
import at.hannibal2.skyhanni.utils.tracker.SkyHanniItemTracker
@@ -43,14 +46,15 @@ object FishingProfitTracker {
private val coinsChatPattern by RepoPattern.pattern(
"fishing.tracker.chat.coins",
- ".* CATCH! §r§bYou found §r§6(?.*) Coins§r§b\\."
+ ".* CATCH! §r§bYou found §r§6(?.*) Coins§r§b\\.",
)
private var lastCatchTime = SimpleTimeMark.farPast()
private val tracker = SkyHanniItemTracker(
"Fishing Profit Tracker",
{ Data() },
- { it.fishing.fishingProfitTracker }) { drawDisplay(it) }
+ { it.fishing.fishingProfitTracker },
+ ) { drawDisplay(it) }
class Data : ItemTrackerData() {
@@ -64,7 +68,7 @@ object FishingProfitTracker {
return listOf(
"§7Caught §e${timesCaught.addSeparators()} §7times.",
- "§7Your catch rate: §c$catchRate"
+ "§7Your catch rate: §c$catchRate",
)
}
@@ -73,7 +77,7 @@ object FishingProfitTracker {
override fun getCoinDescription(item: TrackedItem): List {
val mobKillCoinsFormat = item.totalAmount.shortFormat()
return listOf(
- "§7You fished up §6$mobKillCoinsFormat coins §7already."
+ "§7You fished up §6$mobKillCoinsFormat coins §7already.",
)
}
@@ -117,26 +121,26 @@ object FishingProfitTracker {
return map
}
- private fun drawDisplay(data: Data): List> = buildList {
- addAsSingletonList("§e§lFishing Profit Tracker")
+ private fun drawDisplay(data: Data): List = buildList {
+ addSearchString("§e§lFishing Profit Tracker")
val filter: (NEUInternalName) -> Boolean = addCategories(data)
val profit = tracker.drawItems(data, filter, this)
val fishedCount = data.totalCatchAmount
- addAsSingletonList(
+ add(
Renderable.hoverTips(
"§7Times fished: §e${fishedCount.addSeparators()}",
- listOf("§7You've reeled in §e${fishedCount.addSeparators()} §7catches.")
- )
+ listOf("§7You've reeled in §e${fishedCount.addSeparators()} §7catches."),
+ ).toSearchable(),
)
- addAsSingletonList(tracker.addTotalProfit(profit, data.totalCatchAmount, "catch"))
+ add(tracker.addTotalProfit(profit, data.totalCatchAmount, "catch"))
tracker.addPriceFromButton(this)
}
- private fun MutableList>.addCategories(data: Data): (NEUInternalName) -> Boolean {
+ private fun MutableList.addCategories(data: Data): (NEUInternalName) -> Boolean {
val amounts = getCurrentCategories(data)
checkMissingItems(data)
val list = amounts.keys.toList()
@@ -152,7 +156,7 @@ object FishingProfitTracker {
val id = list.indexOf(currentCategory)
currentCategory = list[(id + 1) % list.size]
tracker.update()
- }
+ },
)
}
@@ -178,7 +182,7 @@ object FishingProfitTracker {
"Loaded $label not in a fishing category",
"Found items missing in itemCategories",
"missingItems" to missingItems,
- noStackTrace = true
+ noStackTrace = true,
)
}
}
@@ -186,15 +190,21 @@ object FishingProfitTracker {
@SubscribeEvent
fun onItemAdd(event: ItemAddEvent) {
if (!isEnabled()) return
+
+ if (event.source == ItemAddManager.Source.COMMAND) {
+ tryAddItem(event.internalName, event.amount, command = true)
+ return
+ }
+
DelayedRun.runDelayed(500.milliseconds) {
- maybeAddItem(event.internalName, event.amount)
+ tryAddItem(event.internalName, event.amount, command = false)
}
}
@SubscribeEvent
fun onChat(event: LorenzChatEvent) {
coinsChatPattern.matchMatcher(event.message) {
- tracker.addCoins(group("coins").formatInt())
+ tryAddItem(NEUInternalName.SKYBLOCK_COIN, group("coins").formatInt(), command = false)
addCatch()
}
}
@@ -221,14 +231,14 @@ object FishingProfitTracker {
lastCatchTime = SimpleTimeMark.farPast()
}
- private fun maybeAddItem(internalName: NEUInternalName, amount: Int) {
+ private fun tryAddItem(internalName: NEUInternalName, amount: Int, command: Boolean) {
if (!FishingAPI.isFishing(checkRodInHand = false)) return
if (!isAllowedItem(internalName)) {
ChatUtils.debug("Ignored non-fishing item pickup: $internalName'")
return
}
- tracker.addItem(internalName, amount)
+ tracker.addItem(internalName, amount, command)
addCatch()
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/SeaCreatureTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/SeaCreatureTracker.kt
index d241b6a19564..b293c2736fbf 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/SeaCreatureTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/tracker/SeaCreatureTracker.kt
@@ -9,14 +9,15 @@ import at.hannibal2.skyhanni.features.fishing.FishingAPI
import at.hannibal2.skyhanni.features.fishing.SeaCreatureManager
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.test.command.ErrorManager
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.CollectionUtils.sumAllValues
import at.hannibal2.skyhanni.utils.ConditionalUtils
import at.hannibal2.skyhanni.utils.LorenzUtils
-import at.hannibal2.skyhanni.utils.LorenzUtils.addButton
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.StringUtils.allLettersFirstUppercase
+import at.hannibal2.skyhanni.utils.renderables.RenderableUtils.addButton
+import at.hannibal2.skyhanni.utils.renderables.Searchable
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
import at.hannibal2.skyhanni.utils.tracker.TrackerData
import com.google.gson.annotations.Expose
@@ -70,8 +71,8 @@ object SeaCreatureTracker {
return map
}
- private fun drawDisplay(data: Data): List> = buildList {
- addAsSingletonList("§7Sea Creature Tracker:")
+ private fun drawDisplay(data: Data): List = buildList {
+ addSearchString("§7Sea Creature Tracker:")
val filter: (String) -> Boolean = addCategories(data)
val realAmount = data.amount.filter { filter(it.key) }
@@ -93,12 +94,12 @@ object SeaCreatureTracker {
" §7$percentage"
} else ""
- addAsSingletonList(" §7- §e${amount.addSeparators()} $displayName$percentageSuffix")
+ addSearchString(" §7- §e${amount.addSeparators()} $displayName$percentageSuffix", displayName)
}
- addAsSingletonList(" §7- §e${total.addSeparators()} §7Total Sea Creatures")
+ addSearchString(" §7- §e${total.addSeparators()} §7Total Sea Creatures")
}
- private fun MutableList>.addCategories(data: Data): (String) -> Boolean {
+ private fun MutableList.addCategories(data: Data): (String) -> Boolean {
val amounts = getCurrentCategories(data)
val list = amounts.keys.toList()
if (currentCategory !in list) {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishDisplay.kt
index d54f7cf48493..ce6352c0ddbe 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishDisplay.kt
@@ -218,7 +218,7 @@ object TrophyFishDisplay {
private fun getItemName(rawName: String): String {
val name = getInternalName(rawName).itemName
- return name.split(" ").dropLast(1).joinToString(" ").replace("§k", "")
+ return name.split(" ").dropLast(1).joinToString(" ")
}
private fun getInternalName(name: String): NEUInternalName {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishManager.kt b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishManager.kt
index 9f3e83f4ed3c..4b91b6240243 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishManager.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/fishing/trophy/TrophyFishManager.kt
@@ -7,7 +7,6 @@ import at.hannibal2.skyhanni.data.jsonobjects.repo.TrophyFishJson
import at.hannibal2.skyhanni.events.NeuProfileDataLoadedEvent
import at.hannibal2.skyhanni.events.RepositoryReloadEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.test.command.ErrorManager
import at.hannibal2.skyhanni.utils.ChatUtils
import net.minecraft.event.HoverEvent
import net.minecraft.util.ChatComponentText
@@ -65,8 +64,8 @@ object TrophyFishManager {
}
private fun updateFromNeuPv(
- savedFishes: MutableMap>,
- neuData: MutableList>,
+ savedFishes: Map>,
+ neuData: List>,
) {
for ((name, rarity, newValue) in neuData) {
val saved = savedFishes[name] ?: continue
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenOptimalSpeed.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenOptimalSpeed.kt
index 5eb89a138ef0..337f64675b66 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenOptimalSpeed.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenOptimalSpeed.kt
@@ -8,9 +8,13 @@ import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.ConditionalUtils
+import at.hannibal2.skyhanni.utils.HypixelCommands
+import at.hannibal2.skyhanni.utils.InventoryUtils
+import at.hannibal2.skyhanni.utils.ItemUtils.getInternalNameOrNull
import at.hannibal2.skyhanni.utils.LorenzColor
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.isRancherSign
+import at.hannibal2.skyhanni.utils.NEUInternalName.Companion.asInternalName
import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
import at.hannibal2.skyhanni.utils.RenderUtils.renderString
import at.hannibal2.skyhanni.utils.SimpleTimeMark
@@ -33,6 +37,7 @@ object GardenOptimalSpeed {
private var sneakingTime = 0.seconds
private val sneaking get() = Minecraft.getMinecraft().thePlayer.isSneaking
private val sneakingPersistent get() = sneakingSince.passedSince() > 5.seconds
+ private val rancherBoots = "RANCHERS_BOOTS".asInternalName()
/**
* This speed value represents the walking speed, not the speed stat.
@@ -175,21 +180,26 @@ object GardenOptimalSpeed {
if (speed != currentSpeed && !recentlySwitchedTool && !recentlyStartedSneaking) warn(speed)
}
- private fun warn(speed: Int) {
+ private fun warn(optimalSpeed: Int) {
if (!Minecraft.getMinecraft().thePlayer.onGround) return
if (GardenAPI.onBarnPlot) return
if (!config.warning) return
+ if (!GardenAPI.isCurrentlyFarming()) return
+ if (InventoryUtils.getBoots()?.getInternalNameOrNull() != rancherBoots) return
if (lastWarnTime.passedSince() < 20.seconds) return
lastWarnTime = SimpleTimeMark.now()
LorenzUtils.sendTitle("§cWrong speed!", 3.seconds)
- cropInHand?.let {
- var text = "Wrong speed for ${it.cropName}: §f$currentSpeed"
- if (sneaking) text += " §7[Sneaking]"
- text += " §e(§f$speed §eis optimal)"
-
- ChatUtils.chat(text)
- }
+ val cropInHand = cropInHand ?: return
+
+ var text = "§cWrong speed while farming ${cropInHand.cropName} detected!"
+ text += "\n§eCurrent Speed: §f$currentSpeed§e, Optimal Speed: §f$optimalSpeed"
+ ChatUtils.clickToActionOrDisable(
+ text,
+ config::warning,
+ actionName = "change the speed",
+ action = { HypixelCommands.setMaxSpeed() },
+ )
}
private fun isRancherOverlayEnabled() = GardenAPI.inGarden() && config.signEnabled
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt
index ebc266692430..cdbb07963d8d 100755
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/GardenYawAndPitch.kt
@@ -5,6 +5,7 @@ import at.hannibal2.skyhanni.config.enums.OutsideSbFeature
import at.hannibal2.skyhanni.events.GardenToolChangeEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.LocationUtils
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzUtils.round
import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
@@ -29,10 +30,7 @@ object GardenYawAndPitch {
if (GardenAPI.toolInHand == null && !config.showWithoutTool) return
val player = Minecraft.getMinecraft().thePlayer
-
- var yaw = player.rotationYaw % 360
- if (yaw < 0) yaw += 360
- if (yaw > 180) yaw -= 360
+ var yaw = LocationUtils.calculatePlayerYaw()
val pitch = player.rotationPitch
if (yaw != lastYaw || pitch != lastPitch) {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt
index a08acac9146e..cebbeef08a71 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/composter/ComposterDisplay.kt
@@ -195,12 +195,11 @@ object ComposterDisplay {
if (IslandType.GARDEN.isInIsland()) {
ChatUtils.chat(warningMessage)
} else {
- ChatUtils.clickableChat(
+ ChatUtils.clickToActionOrDisable(
warningMessage,
- onClick = {
- HypixelCommands.warp("garden")
- },
- "§eClick to warp to the garden!",
+ config::warnAlmostClose,
+ actionName = "warp to the Garden",
+ action = { HypixelCommands.warp("garden") },
)
}
LorenzUtils.sendTitle("§eComposter Warning!", 3.seconds)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/ArmorDropTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/ArmorDropTracker.kt
index b55ed769dda8..90c68a046980 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/ArmorDropTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/ArmorDropTracker.kt
@@ -13,13 +13,14 @@ import at.hannibal2.skyhanni.events.SecondPassedEvent
import at.hannibal2.skyhanni.features.garden.CropType
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc
import at.hannibal2.skyhanni.utils.InventoryUtils
import at.hannibal2.skyhanni.utils.ItemUtils.getInternalName
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.SimpleTimeMark
+import at.hannibal2.skyhanni.utils.renderables.Searchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
import at.hannibal2.skyhanni.utils.tracker.TrackerData
@@ -83,11 +84,11 @@ object ArmorDropTracker {
}
}
- private fun drawDisplay(data: Data): List> = buildList {
- addAsSingletonList("§7Armor Drop Tracker:")
+ private fun drawDisplay(data: Data): List = buildList {
+ addSearchString("§7Armor Drop Tracker:")
for ((drop, amount) in data.drops.sortedDesc()) {
val dropName = drop.dropName
- addAsSingletonList(" §7- §e${amount.addSeparators()}x $dropName")
+ addSearchString(" §7- §e${amount.addSeparators()}x $dropName", dropName)
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/DicerRngDropTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/DicerRngDropTracker.kt
index 7d9f2587e3fd..8cc8c55cb32a 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/DicerRngDropTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/DicerRngDropTracker.kt
@@ -10,12 +10,15 @@ import at.hannibal2.skyhanni.features.garden.CropType
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.CollectionUtils.addOrPut
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.CollectionUtils.sortedDesc
import at.hannibal2.skyhanni.utils.ConditionalUtils
import at.hannibal2.skyhanni.utils.ItemUtils.name
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.renderables.Searchable
+import at.hannibal2.skyhanni.utils.renderables.toSearchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.SkyHanniTracker
import at.hannibal2.skyhanni.utils.tracker.TrackerData
@@ -120,30 +123,28 @@ object DicerRngDropTracker {
}
}
- private fun drawDisplay(data: Data) = buildList {
+ private fun drawDisplay(data: Data) = buildList {
val cropInHand = cropInHand ?: return@buildList
val topLine = mutableListOf()
topLine.add(Renderable.itemStack(cropInHand.icon))
topLine.add(Renderable.string("§7Dicer Tracker:"))
- add(listOf(Renderable.horizontalContainer(topLine)))
+ add(Renderable.horizontalContainer(topLine).toSearchable())
val items = data.drops[cropInHand] ?: return@buildList
- val list = mutableListOf()
if (config.compact.get()) {
val compactLine = items.sortedDesc().map { (rarity, amount) ->
"§${rarity.colorCode}${amount.addSeparators()}"
}.joinToString("§7/")
- list.add(Renderable.string(compactLine))
+ addSearchString(compactLine)
} else {
for ((rarity, amount) in items.sortedDesc()) {
val colorCode = rarity.colorCode
val displayName = rarity.displayName
- list.add(Renderable.string(" §7- §e${amount.addSeparators()}x §$colorCode$displayName"))
+ addSearchString(" §7- §e${amount.addSeparators()}x §$colorCode$displayName", displayName)
}
}
- add(listOf(Renderable.verticalContainer(list)))
}
private var cropInHand: CropType? = null
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt
index 7133cf1fb301..222e75f4f296 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/FarmingWeightDisplay.kt
@@ -511,7 +511,7 @@ object FarmingWeightDisplay {
"Error loading user farming weight\n" +
"§eLoading the farming weight data from elitebot.dev failed!\n" +
"§eYou can re-enter the garden to try to fix the problem.\n" +
- "§cIf this message repeats, please report it on Discord!\n",
+ "§cIf this message repeats, please report it on Discord",
"url" to url,
"apiResponse" to apiResponse,
"localProfile" to localProfile,
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt
index b52b9f62b9dd..c37eb41b7cdf 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropMilestoneDisplay.kt
@@ -120,13 +120,7 @@ object GardenCropMilestoneDisplay {
val addedCounter = (counter - old).toInt()
FarmingWeightDisplay.addCrop(crop, addedCounter)
update()
- // Farming Simulator: There is a 25% chance for Mathematical Hoes and the Cultivating Enchantment to count twice.
- // 0.8 = 1 / 1.25
- crop.setCounter(
- crop.getCounter() + if (GardenCropSpeed.finneganPerkActive()) {
- (addedCounter.toDouble() * 0.8).toInt()
- } else addedCounter
- )
+ crop.setCounter(crop.getCounter() + addedCounter)
}
cultivatingData[crop] = counter
} catch (e: Throwable) {
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt
index f4d9542293e4..141c93ff102c 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/farming/GardenCropSpeed.kt
@@ -177,8 +177,6 @@ object GardenCropSpeed {
secondsStopped = 0
}
- fun finneganPerkActive() = Perk.FARMING_SIMULATOR.isActive
-
fun isEnabled() = GardenAPI.inGarden()
fun CropType.getSpeed() = cropsPerSecond?.get(this)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt
index c508e08a50eb..d8754fd81e70 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestFinder.kt
@@ -4,6 +4,7 @@ import at.hannibal2.skyhanni.api.event.HandleEvent
import at.hannibal2.skyhanni.config.features.garden.pests.PestFinderConfig.VisibilityType
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.IslandChangeEvent
+import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.LorenzKeyPressEvent
import at.hannibal2.skyhanni.events.LorenzRenderWorldEvent
import at.hannibal2.skyhanni.events.garden.pests.PestUpdateEvent
@@ -19,8 +20,10 @@ import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.HypixelCommands
import at.hannibal2.skyhanni.utils.LorenzColor
+import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.LorenzVec
import at.hannibal2.skyhanni.utils.NEUItems
+import at.hannibal2.skyhanni.utils.RegexUtils.matches
import at.hannibal2.skyhanni.utils.RenderUtils.drawDynamicText
import at.hannibal2.skyhanni.utils.RenderUtils.drawWaypointFilled
import at.hannibal2.skyhanni.utils.RenderUtils.exactPlayerEyeLocation
@@ -28,6 +31,7 @@ import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderables
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils
import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import net.minecraft.client.Minecraft
import net.minecraftforge.fml.common.eventhandler.EventPriority
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
@@ -39,6 +43,10 @@ object PestFinder {
private val config get() = PestAPI.config.pestFinder
private var display = emptyList()
+ val noPestsChatPattern by RepoPattern.pattern(
+ "chat.garden.no.pest",
+ "§cThere are not any Pests on your Garden right now! Keep farming!",
+ )
@HandleEvent
fun onPestUpdate(event: PestUpdateEvent) {
@@ -68,26 +76,28 @@ object PestFinder {
"§7Pests Found: §e" + if (isInaccurate) "Unknown" else pests,
"§7In plot §b$plotName",
"",
- "§eClick here to warp!"
+ "§eClick here to warp!",
),
onClick = {
plot.sendTeleportTo()
- }
+ },
)
add(renderable)
}
if (PestAPI.getInfestedPlots().isEmpty() && PestAPI.scoreboardPests != 0) {
add(Renderable.string("§e${PestAPI.scoreboardPests} §6Bugged pests!"))
- add(Renderable.clickAndHover(
- "§cTry opening your plots menu.",
- listOf(
- "Runs /desk."
+ add(
+ Renderable.clickAndHover(
+ "§cTry opening your plots menu.",
+ listOf(
+ "Runs /desk.",
+ ),
+ onClick = {
+ HypixelCommands.gardenDesk()
+ },
),
- onClick = {
- HypixelCommands.gardenDesk()
- }
- ))
+ )
}
}
@@ -151,12 +161,20 @@ object PestFinder {
pests
) + " §c$pestsName §7in §b$plotName"
event.drawDynamicText(
- location, text, 1.5
+ location, text, 1.5,
)
}
private var lastKeyPress = SimpleTimeMark.farPast()
+ @SubscribeEvent
+ fun onChat(event: LorenzChatEvent) {
+ if (!GardenAPI.inGarden()) return
+ if (!config.noPestTitle) return
+
+ if (noPestsChatPattern.matches(event.message)) LorenzUtils.sendTitle("§eNo pests!", 2.seconds)
+ }
+
@SubscribeEvent
fun onKeyClick(event: LorenzKeyPressEvent) {
if (!GardenAPI.inGarden()) return
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleLine.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleLine.kt
index e81486ecd944..ff9af9cff523 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleLine.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestParticleLine.kt
@@ -94,6 +94,7 @@ object PestParticleLine {
}
private fun showMiddle(event: LorenzRenderWorldEvent) {
+ if (!config.showMiddle) return
if (locations.size <= 0) return
val plot = GardenPlotAPI.getCurrentPlot() ?: return
val middle = plot.middle.copy(y = LocationUtils.playerLocation().y)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestProfitTracker.kt b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestProfitTracker.kt
index 5f630335b5c4..19506109ec9e 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestProfitTracker.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/garden/pests/PestProfitTracker.kt
@@ -2,14 +2,16 @@ package at.hannibal2.skyhanni.features.garden.pests
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.data.IslandType
+import at.hannibal2.skyhanni.data.ItemAddManager
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.IslandChangeEvent
+import at.hannibal2.skyhanni.events.ItemAddEvent
import at.hannibal2.skyhanni.events.LorenzChatEvent
import at.hannibal2.skyhanni.events.PurseChangeCause
import at.hannibal2.skyhanni.events.PurseChangeEvent
import at.hannibal2.skyhanni.features.garden.GardenAPI
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
-import at.hannibal2.skyhanni.utils.CollectionUtils.addAsSingletonList
+import at.hannibal2.skyhanni.utils.CollectionUtils.addSearchString
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.NEUInternalName
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
@@ -17,6 +19,8 @@ import at.hannibal2.skyhanni.utils.NumberUtil.shortFormat
import at.hannibal2.skyhanni.utils.RegexUtils.matchMatcher
import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.renderables.Renderable
+import at.hannibal2.skyhanni.utils.renderables.Searchable
+import at.hannibal2.skyhanni.utils.renderables.toSearchable
import at.hannibal2.skyhanni.utils.repopatterns.RepoPattern
import at.hannibal2.skyhanni.utils.tracker.ItemTrackerData
import at.hannibal2.skyhanni.utils.tracker.SkyHanniItemTracker
@@ -37,14 +41,15 @@ object PestProfitTracker {
*/
private val pestRareDropPattern by patternGroup.pattern(
"raredrop",
- "§6§l(?:RARE|PET) DROP! (?:§r)?(?- .+) §6\\(§6\\+.*☘\\)"
+ "§6§l(?:RARE|PET) DROP! (?:§r)?(?
- .+) §6\\(§6\\+.*☘\\)",
)
private var lastPestKillTime = SimpleTimeMark.farPast()
private val tracker = SkyHanniItemTracker(
"Pest Profit Tracker",
{ Data() },
- { it.garden.pestProfitTracker }) { drawDisplay(it) }
+ { it.garden.pestProfitTracker },
+ ) { drawDisplay(it) }
class Data : ItemTrackerData() {
override fun resetItems() {
@@ -56,7 +61,7 @@ object PestProfitTracker {
val dropRate = LorenzUtils.formatPercentage(percentage.coerceAtMost(1.0))
return listOf(
"§7Dropped §e${timesGained.addSeparators()} §7times.",
- "§7Your drop rate: §c$dropRate."
+ "§7Your drop rate: §c$dropRate.",
)
}
@@ -66,7 +71,7 @@ object PestProfitTracker {
val pestsCoinsFormat = item.totalAmount.shortFormat()
return listOf(
"§7Killing pests gives you coins.",
- "§7You got §6$pestsCoinsFormat coins §7that way."
+ "§7You got §6$pestsCoinsFormat coins §7that way.",
)
}
@@ -74,6 +79,16 @@ object PestProfitTracker {
var totalPestsKills = 0L
}
+ @SubscribeEvent
+ fun onItemAdd(event: ItemAddEvent) {
+ if (!isEnabled()) return
+
+ val internalName = event.internalName
+ if (event.source == ItemAddManager.Source.COMMAND) {
+ tryAddItem(internalName, event.amount, command = true)
+ }
+ }
+
@SubscribeEvent
fun onChat(event: LorenzChatEvent) {
if (!isEnabled()) return
@@ -81,18 +96,22 @@ object PestProfitTracker {
val amount = group("amount").toInt()
val internalName = NEUInternalName.fromItemNameOrNull(group("item")) ?: return
- tracker.addItem(internalName, amount)
+ tryAddItem(internalName, amount, command = false)
addKill()
if (config.hideChat) event.blockedReason = "pest_drop"
}
pestRareDropPattern.matchMatcher(event.message) {
val internalName = NEUInternalName.fromItemNameOrNull(group("item")) ?: return
- tracker.addItem(internalName, 1)
+ tryAddItem(internalName, 1, command = false)
// pests always have guaranteed loot, therefore there's no need to add kill here
}
}
+ private fun tryAddItem(internalName: NEUInternalName, amount: Int, command: Boolean) {
+ tracker.addItem(internalName, amount, command)
+ }
+
private fun addKill() {
tracker.modify {
it.totalPestsKills++
@@ -100,18 +119,18 @@ object PestProfitTracker {
lastPestKillTime = SimpleTimeMark.now()
}
- private fun drawDisplay(data: Data): List
> = buildList {
- addAsSingletonList("§e§lPest Profit Tracker")
+ private fun drawDisplay(data: Data): List = buildList {
+ addSearchString("§e§lPest Profit Tracker")
val profit = tracker.drawItems(data, { true }, this)
val pestsKilled = data.totalPestsKills
- addAsSingletonList(
+ add(
Renderable.hoverTips(
"§7Pests killed: §e${pestsKilled.addSeparators()}",
- listOf("§7You killed pests §e${pestsKilled.addSeparators()} §7times.")
- )
+ listOf("§7You killed pests §e${pestsKilled.addSeparators()} §7times."),
+ ).toSearchable(),
)
- addAsSingletonList(tracker.addTotalProfit(profit, data.totalPestsKills, "kill"))
+ add(tracker.addTotalProfit(profit, data.totalPestsKills, "kill"))
tracker.addPriceFromButton(this)
}
@@ -131,7 +150,7 @@ object PestProfitTracker {
val coins = event.coins
if (coins > 1000) return
if (event.reason == PurseChangeCause.GAIN_MOB_KILL && lastPestKillTime.passedSince() < 2.seconds) {
- tracker.addCoins(coins.toInt())
+ tryAddItem(NEUInternalName.SKYBLOCK_COIN, coins.toInt(), command = false)
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/MovableXpBar.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/MovableXpBar.kt
new file mode 100644
index 000000000000..4c9f59e12eda
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/MovableXpBar.kt
@@ -0,0 +1,43 @@
+package at.hannibal2.skyhanni.features.gui
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.data.GuiEditManager
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.transform
+import net.minecraft.client.Minecraft
+import net.minecraft.client.renderer.GlStateManager
+import net.minecraftforge.client.event.RenderGameOverlayEvent
+import net.minecraftforge.fml.common.eventhandler.EventPriority
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+@SkyHanniModule
+object MovableXpBar {
+
+ private val config get() = SkyHanniMod.feature.gui.xpBar
+
+ private var post = false
+
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ fun onRenderXpBar(event: RenderGameOverlayEvent.Pre) {
+ if (event.type != RenderGameOverlayEvent.ElementType.EXPERIENCE || !isEnabled()) return
+ post = true
+ GlStateManager.pushMatrix()
+ val scaled = event.resolution
+ val x = scaled.scaledWidth / 2 - 91
+ val y = scaled.scaledHeight - 29
+ config.position.transform()
+ GlStateManager.translate(-x.toFloat(), -y.toFloat(), 0f) // Must be after transform to work with scaling
+ GuiEditManager.add(config.position, "Xp Bar", 182 - 1, 5 - 1) // -1 since the editor for some reason add +1
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ fun onRenderXpBar(event: RenderGameOverlayEvent.Post) {
+ if (event.type != RenderGameOverlayEvent.ElementType.EXPERIENCE || !post) return
+ GlStateManager.popMatrix()
+ post = false
+ }
+
+ private fun isEnabled() = (LorenzUtils.inSkyBlock || (Minecraft.getMinecraft().thePlayer != null && config.showOutsideSkyblock)) &&
+ config.enabled
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/TabWidgetDisplay.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/TabWidgetDisplay.kt
new file mode 100644
index 000000000000..2e598923d426
--- /dev/null
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/TabWidgetDisplay.kt
@@ -0,0 +1,79 @@
+package at.hannibal2.skyhanni.features.gui
+
+import at.hannibal2.skyhanni.SkyHanniMod
+import at.hannibal2.skyhanni.config.core.config.Position
+import at.hannibal2.skyhanni.data.model.TabWidget
+import at.hannibal2.skyhanni.events.GuiRenderEvent
+import at.hannibal2.skyhanni.events.ProfileJoinEvent
+import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
+import at.hannibal2.skyhanni.test.command.ErrorManager
+import at.hannibal2.skyhanni.utils.LorenzUtils
+import at.hannibal2.skyhanni.utils.RenderUtils.renderStrings
+import at.hannibal2.skyhanni.utils.StringUtils.allLettersFirstUppercase
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+
+enum class TabWidgetDisplay(private val configName: String?, vararg val widgets: TabWidget) {
+ SOULFLOW(null, TabWidget.SOULFLOW),
+ COINS("Bank and Interest", TabWidget.BANK, TabWidget.INTEREST),
+ SB_LEVEL("Skyblock Level", TabWidget.SB_LEVEL),
+ PROFILE(null, TabWidget.PROFILE),
+ PLAYER_LIST("Players", TabWidget.PLAYER_LIST),
+ PET(null, TabWidget.PET),
+ PET_TRAINING("Pet Upgrade Info", TabWidget.PET_SITTER, TabWidget.PET_TRAINING),
+ STATS(null, TabWidget.STATS, TabWidget.DUNGEON_SKILLS_AND_STATS),
+ DUNGEON_TEAM("Dungeon Info about every person", TabWidget.DUNGEON_PARTY),
+ DUNGEON_PUZZLE("Dungeon Info about puzzles", TabWidget.DUNGEON_PUZZLE),
+ DUNGEON_OVERALL("Dungeon General Info (very long)", TabWidget.DUNGEON_STATS),
+ BESTIARY(null, TabWidget.BESTIARY),
+ DRAGON("Dragon Fight Info", TabWidget.DRAGON),
+ PROTECTOR("Protector State", TabWidget.PROTECTOR),
+ SHEN_RIFT("Shen's Auction inside the Rift", TabWidget.RIFT_SHEN),
+ MINION("Minion Info", TabWidget.MINION),
+ COLLECTION(null, TabWidget.COLLECTION),
+ TIMERS(null, TabWidget.TIMERS),
+ FIRE_SALE(null, TabWidget.FIRE_SALE),
+ RAIN("Park Rain", TabWidget.RAIN),
+ ;
+
+ val position get() = config.displayPositions[ordinal]
+
+ override fun toString(): String {
+ return configName ?: name.lowercase().allLettersFirstUppercase()
+ }
+
+ @SkyHanniModule
+ companion object {
+
+ private val config get() = SkyHanniMod.feature.gui.tabWidget
+
+ private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled
+
+ @SubscribeEvent
+ fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
+ if (!isEnabled()) return
+ if (config?.displayPositions == null) return
+ config.display.forEach { widget ->
+ widget.position.renderStrings(
+ widget.widgets.flatMap { it.lines },
+ posLabel = "Display Widget: ${widget.name}",
+ )
+ }
+ }
+
+ @SubscribeEvent
+ fun onJoin(event: ProfileJoinEvent) {
+ // Validation that the displayPositions in the config is correct
+ val sizeDiff = TabWidgetDisplay.entries.size - config.displayPositions.size
+ if (sizeDiff == 0) return
+ if (sizeDiff < 0) {
+ ErrorManager.skyHanniError(
+ "Invalid State of config.displayPositions",
+ "Display" to TabWidgetDisplay.entries,
+ "Positions" to config.displayPositions,
+ )
+ } else {
+ config.displayPositions.addAll(generateSequence { Position() }.take(sizeDiff))
+ }
+ }
+ }
+}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt
index df8adab371e3..a3da970c660f 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboard.kt
@@ -1,32 +1,26 @@
//
// TODO LIST
// V2 RELEASE
-// - Soulflow API
// - Bank API (actually maybe not, I like the current design)
-// - beacon power
-// - skyblock level
-// - more bg options (round, blurr, outline)
// - countdown events like fishing festival + fiesta when its not on tablist
-// - CookieAPI https://discord.com/channels/997079228510117908/1162844830360146080/1195695210433351821
-// - Rng meter display
-// - option to hide coins earned
+// - improve hide coin difference to also work with bits, motes, etc
// - color options in the purse etc lines
// - choose the amount of decimal places in shorten nums
-// - more anchor points (alignment enums in renderutils)
-// - 24h instead of 12h for skyblock time
-// - only alert for lines that exist longer than 1s
//
package at.hannibal2.skyhanni.features.gui.customscoreboard
import at.hannibal2.skyhanni.SkyHanniMod
import at.hannibal2.skyhanni.config.ConfigUpdaterMigrator
+import at.hannibal2.skyhanni.config.enums.OutsideSbFeature
+import at.hannibal2.skyhanni.data.ScoreboardData
import at.hannibal2.skyhanni.events.ConfigLoadEvent
import at.hannibal2.skyhanni.events.DebugDataCollectEvent
import at.hannibal2.skyhanni.events.GuiPositionMovedEvent
import at.hannibal2.skyhanni.events.GuiRenderEvent
import at.hannibal2.skyhanni.events.LorenzTickEvent
import at.hannibal2.skyhanni.events.LorenzWorldChangeEvent
+import at.hannibal2.skyhanni.events.ScoreboardUpdateEvent
import at.hannibal2.skyhanni.skyhannimodule.SkyHanniModule
import at.hannibal2.skyhanni.utils.ChatUtils
import at.hannibal2.skyhanni.utils.ConditionalUtils
@@ -34,14 +28,17 @@ import at.hannibal2.skyhanni.utils.DelayedRun.runDelayed
import at.hannibal2.skyhanni.utils.LorenzUtils
import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment
import at.hannibal2.skyhanni.utils.RenderUtils.VerticalAlignment
-import at.hannibal2.skyhanni.utils.RenderUtils.renderStringsAlignedWidth
+import at.hannibal2.skyhanni.utils.RenderUtils.renderRenderable
+import at.hannibal2.skyhanni.utils.SimpleTimeMark
import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase
import at.hannibal2.skyhanni.utils.TabListData
+import at.hannibal2.skyhanni.utils.renderables.Renderable
import com.google.gson.JsonArray
import com.google.gson.JsonPrimitive
import net.minecraftforge.client.GuiIngameForge
import net.minecraftforge.client.event.RenderGameOverlayEvent
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
typealias ScoreboardElementType = Pair
@@ -53,36 +50,60 @@ object CustomScoreboard {
private var cache = emptyList()
private val guiName = "Custom Scoreboard"
+ // Cached scoreboard data, only update after no change for 300ms
+ var activeLines = emptyList()
+
+ // Most recent scoreboard state, not in use until cached
+ private var mostRecentLines = emptyList()
+ private var lastScoreboardUpdate = SimpleTimeMark.farFuture()
+
@SubscribeEvent
fun onRenderOverlay(event: GuiRenderEvent.GuiOverlayRenderEvent) {
if (!isEnabled()) return
if (display.isEmpty()) return
- RenderBackground().renderBackground()
-
val render =
- if (!TabListData.fullyLoaded && displayConfig.cacheScoreboardOnIslandSwitch && cache.isNotEmpty()) {
+ if (LorenzUtils.inSkyBlock && !TabListData.fullyLoaded && displayConfig.cacheScoreboardOnIslandSwitch && cache.isNotEmpty()) {
cache
} else {
display
}
- config.position.renderStringsAlignedWidth(
- render,
- posLabel = guiName,
- extraSpace = displayConfig.lineSpacing - 10,
+
+ val textRenderable = Renderable.verticalContainer(
+ render.map { Renderable.string(it.first, horizontalAlign = it.second) },
+ displayConfig.lineSpacing - 10,
+ horizontalAlign = HorizontalAlignment.CENTER,
+ verticalAlign = VerticalAlignment.CENTER,
)
+
+ val finalRenderable = RenderBackground.addBackground(textRenderable)
+
+ RenderBackground.updatePosition(finalRenderable)
+
+ config.position.renderRenderable(finalRenderable, posLabel = guiName)
}
@SubscribeEvent
fun onGuiPositionMoved(event: GuiPositionMovedEvent) {
if (event.guiName == guiName) {
with(alignmentConfig) {
- if (horizontalAlignment != HorizontalAlignment.DONT_ALIGN
- || verticalAlignment != VerticalAlignment.DONT_ALIGN
+ if (horizontalAlignment != HorizontalAlignment.DONT_ALIGN ||
+ verticalAlignment != VerticalAlignment.DONT_ALIGN
) {
+ val tempHori = horizontalAlignment
+ val tempVert = verticalAlignment
+
horizontalAlignment = HorizontalAlignment.DONT_ALIGN
verticalAlignment = VerticalAlignment.DONT_ALIGN
- ChatUtils.chat("Disabled Custom Scoreboard auto-alignment.")
+ ChatUtils.clickableChat(
+ "Disabled Custom Scoreboard auto-alignment. Click here to undo this action!",
+ oneTimeClick = true,
+ onClick = {
+ horizontalAlignment = tempHori
+ verticalAlignment = tempVert
+ ChatUtils.chat("Enabled Custom Scoreboard auto-alignment.")
+ },
+ )
}
}
}
@@ -92,8 +113,16 @@ object CustomScoreboard {
fun onTick(event: LorenzTickEvent) {
if (!isEnabled()) return
+ // We want to update the scoreboard as soon as we have new data, not 5 ticks delayed
+ var dirty = false
+ if (lastScoreboardUpdate.passedSince() > 300.milliseconds) {
+ activeLines = mostRecentLines
+ lastScoreboardUpdate = SimpleTimeMark.farFuture()
+ dirty = true
+ }
+
// Creating the lines
- if (event.isMod(5)) {
+ if (event.isMod(5) || dirty) {
display = createLines().removeEmptyLinesFromEdges()
if (TabListData.fullyLoaded) {
cache = display.toList()
@@ -101,9 +130,16 @@ object CustomScoreboard {
}
// Remove Known Lines, so we can get the unknown ones
- UnknownLinesHandler.handleUnknownLines()
+ if (LorenzUtils.inSkyBlock && displayConfig.useCustomLines) UnknownLinesHandler.handleUnknownLines()
+ }
+
+ @SubscribeEvent
+ fun onScoreboardChange(event: ScoreboardUpdateEvent) {
+ mostRecentLines = event.scoreboard
+ lastScoreboardUpdate = SimpleTimeMark.now()
}
+
internal val config get() = SkyHanniMod.feature.gui.customScoreboard
internal val displayConfig get() = config.display
internal val alignmentConfig get() = displayConfig.alignment
@@ -116,12 +152,27 @@ object CustomScoreboard {
internal val informationFilteringConfig get() = config.informationFiltering
internal val backgroundConfig get() = config.background
- private fun createLines() = buildList {
+ private fun createLines() = when {
+ !LorenzUtils.inSkyBlock -> addAllNonSkyBlockLines()
+ !displayConfig.useCustomLines -> addDefaultSkyBlockLines()
+ else -> addCustomSkyBlockLines()
+ }
+
+ private fun addAllNonSkyBlockLines() = buildList {
+ addAll(ScoreboardElement.TITLE.getVisiblePair())
+ addAll(activeLines.map { it to HorizontalAlignment.LEFT })
+ }
+
+ private fun addDefaultSkyBlockLines() = buildList {
+ add(ScoreboardData.objectiveTitle to displayConfig.titleAndFooter.alignTitleAndFooter)
+ addAll(activeLines.map { it to HorizontalAlignment.LEFT })
+ }
+
+ private fun addCustomSkyBlockLines() = buildList {
for (element in config.scoreboardEntries) {
val lines = element.getVisiblePair()
if (lines.isEmpty()) continue
- // Hide consecutive empty lines
if (
informationFilteringConfig.hideConsecutiveEmptyLines &&
lines.first().first == "" && lastOrNull()?.first?.isEmpty() == true
@@ -129,13 +180,11 @@ object CustomScoreboard {
continue
}
- // Adds empty lines
if (lines.first().first == "") {
add("" to HorizontalAlignment.LEFT)
continue
}
- // Does not display this line
if (lines.any { it.first == "" }) {
continue
}
@@ -171,7 +220,11 @@ object CustomScoreboard {
@SubscribeEvent
fun onConfigLoad(event: ConfigLoadEvent) {
- ConditionalUtils.onToggle(config.enabled, displayConfig.hideVanillaScoreboard) {
+ ConditionalUtils.onToggle(
+ config.enabled,
+ displayConfig.hideVanillaScoreboard,
+ SkyHanniMod.feature.misc.showOutsideSB,
+ ) {
if (!isHideVanillaScoreboardEnabled()) dirty = true
}
}
@@ -179,7 +232,7 @@ object CustomScoreboard {
@SubscribeEvent
fun onWorldChange(event: LorenzWorldChangeEvent) {
runDelayed(2.seconds) {
- if (!LorenzUtils.inSkyBlock) dirty = true
+ if (!LorenzUtils.inSkyBlock || !(LorenzUtils.onHypixel && OutsideSbFeature.CUSTOM_SCOREBOARD.isSelected())) dirty = true
}
}
@@ -201,7 +254,9 @@ object CustomScoreboard {
}
}
- private fun isEnabled() = LorenzUtils.inSkyBlock && config.enabled.get()
+ private fun isEnabled() =
+ (LorenzUtils.inSkyBlock || (OutsideSbFeature.CUSTOM_SCOREBOARD.isSelected() && LorenzUtils.onHypixel)) && config.enabled.get()
+
private fun isHideVanillaScoreboardEnabled() = isEnabled() && displayConfig.hideVanillaScoreboard.get()
@SubscribeEvent
@@ -293,5 +348,12 @@ object CustomScoreboard {
array.add(JsonPrimitive(ScoreboardEvent.NEW_YEAR.name))
array
}
+ event.move(
+ 57,
+ "$displayPrefix.titleAndFooter.useHypixelTitleAnimation",
+ "$displayPrefix.titleAndFooter.useCustomTitle",
+ ) {
+ JsonPrimitive(!it.asBoolean)
+ }
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt
index d1eaf2abd571..4cfc403c421f 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/CustomScoreboardUtils.kt
@@ -3,7 +3,6 @@ package at.hannibal2.skyhanni.features.gui.customscoreboard
import at.hannibal2.skyhanni.config.features.gui.customscoreboard.DisplayConfig
import at.hannibal2.skyhanni.data.BitsAPI
import at.hannibal2.skyhanni.data.HypixelData
-import at.hannibal2.skyhanni.data.ScoreboardData
import at.hannibal2.skyhanni.features.bingo.BingoAPI
import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.displayConfig
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
@@ -23,12 +22,12 @@ object CustomScoreboardUtils {
pattern.matchMatcher(line) {
group(group)
}
- } ?: "0"
+ }
fun getProfileTypeSymbol() = when {
HypixelData.ironman -> "§7♲ "
HypixelData.stranded -> "§a☀ "
- HypixelData.bingo -> ScoreboardData.sidebarLinesFormatted.firstNotNullOfOrNull {
+ HypixelData.bingo -> CustomScoreboard.activeLines.firstNotNullOfOrNull {
BingoAPI.getIconFromScoreboard(it)?.plus(" ")
} ?: "§e❤ "
@@ -44,7 +43,7 @@ object CustomScoreboardUtils {
internal fun String.formatNum() = this.formatDouble().formatNum()
- internal fun getMotes() = getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.motesPattern, "motes")
+ internal fun getMotes() = getGroupFromPattern(CustomScoreboard.activeLines, ScoreboardPattern.motesPattern, "motes")
internal fun getBank() = getGroupFromPattern(TabListData.getTabList(), ScoreboardPattern.bankPattern, "bank")
internal fun getBits() = BitsAPI.bits.coerceAtLeast(0).formatNum()
@@ -58,15 +57,15 @@ object CustomScoreboardUtils {
}
internal fun getCopper() =
- getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.copperPattern, "copper")
+ getGroupFromPattern(CustomScoreboard.activeLines, ScoreboardPattern.copperPattern, "copper")
internal fun getGems() = getGroupFromPattern(TabListData.getTabList(), ScoreboardPattern.gemsPattern, "gems")
internal fun getHeat() =
- getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.heatPattern, "heat")
+ getGroupFromPattern(CustomScoreboard.activeLines, ScoreboardPattern.heatPattern, "heat")
internal fun getNorthStars() =
- getGroupFromPattern(ScoreboardData.sidebarLinesFormatted, ScoreboardPattern.northstarsPattern, "northstars")
+ getGroupFromPattern(CustomScoreboard.activeLines, ScoreboardPattern.northstarsPattern, "northstars")
class UndetectedScoreboardLines(message: String) : Exception(message)
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt
index ebbb7fa3074c..14d29d6ac015 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/RenderBackground.kt
@@ -1,139 +1,118 @@
package at.hannibal2.skyhanni.features.gui.customscoreboard
-import at.hannibal2.skyhanni.config.core.config.Position
+import at.hannibal2.skyhanni.config.features.gui.customscoreboard.BackgroundConfig
import at.hannibal2.skyhanni.data.GuiEditManager
import at.hannibal2.skyhanni.data.GuiEditManager.getAbsX
import at.hannibal2.skyhanni.data.GuiEditManager.getAbsY
-import at.hannibal2.skyhanni.data.GuiEditManager.getDummySize
+import at.hannibal2.skyhanni.features.gui.customscoreboard.CustomScoreboard.backgroundConfig
import at.hannibal2.skyhanni.utils.ColorUtils.toChromaColor
import at.hannibal2.skyhanni.utils.RenderUtils
+import at.hannibal2.skyhanni.utils.renderables.Renderable
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.ScaledResolution
-import net.minecraft.client.renderer.GlStateManager
import net.minecraft.util.ResourceLocation
-import org.lwjgl.opengl.GL11
-class RenderBackground {
+object RenderBackground {
- fun renderBackground() {
- val alignmentConfig = CustomScoreboard.alignmentConfig
- val backgroundConfig = CustomScoreboard.backgroundConfig
- val outlineConfig = backgroundConfig.outline
- val position = CustomScoreboard.config.position
- val border = backgroundConfig.borderSize
-
- val x = position.getAbsX()
- val y = position.getAbsY()
+ private val textureLocation by lazy { ResourceLocation("skyhanni", "scoreboard.png") }
- val elementWidth = position.getDummySize().x
- val elementHeight = position.getDummySize().y
+ internal fun addBackground(renderable: Renderable): Renderable {
+ with(backgroundConfig) {
+ if (!backgroundConfig.enabled) return renderable
- // Update the position to the alignment options
- if (
- alignmentConfig.horizontalAlignment != RenderUtils.HorizontalAlignment.DONT_ALIGN
- || alignmentConfig.verticalAlignment != RenderUtils.VerticalAlignment.DONT_ALIGN
- ) {
- position.set(updatePosition(position))
- }
+ val backgroundRenderable = createBackground(renderable)
- if (GuiEditManager.isInGui()) return
+ if (!outline.enabled) return backgroundRenderable
- GlStateManager.pushMatrix()
-
- GlStateManager.color(1f, 1f, 1f, 1f)
- GL11.glDepthMask(false)
-
- if (backgroundConfig.enabled) {
- if (backgroundConfig.useCustomBackgroundImage) {
- val textureLocation = ResourceLocation("skyhanni", "scoreboard.png")
- Minecraft.getMinecraft().textureManager.bindTexture(textureLocation)
-
- RenderUtils.drawRoundTexturedRect(
- x - border,
- y - border,
- elementWidth + border * 3,
- elementHeight + border * 2,
- GL11.GL_NEAREST,
- backgroundConfig.roundedCornerSmoothness,
- )
- } else {
- RenderUtils.drawRoundRect(
- x - border,
- y - border,
- elementWidth + border * 3,
- elementHeight + border * 2,
- backgroundConfig.color.toChromaColor().rgb,
- backgroundConfig.roundedCornerSmoothness,
- )
- }
- if (outlineConfig.enabled) {
- RenderUtils.drawRoundRectOutline(
- x - border,
- y - border,
- elementWidth + border * 3,
- elementHeight + border * 2,
- outlineConfig.colorTop.toChromaColor().rgb,
- outlineConfig.colorBottom.toChromaColor().rgb,
- outlineConfig.thickness,
- backgroundConfig.roundedCornerSmoothness,
- outlineConfig.blur,
- )
- }
+ return Renderable.drawInsideRoundedRectOutline(
+ backgroundRenderable,
+ 0,
+ backgroundConfig.roundedCornerSmoothness,
+ 1,
+ outline.colorTop.toChromaColor().rgb,
+ outline.colorBottom.toChromaColor().rgb,
+ outline.thickness,
+ outline.blur,
+ horizontalAlign = RenderUtils.HorizontalAlignment.CENTER,
+ verticalAlign = RenderUtils.VerticalAlignment.CENTER,
+ )
}
- GL11.glDepthMask(true)
- GlStateManager.popMatrix()
}
- private fun updatePosition(position: Position): Position {
- val alignmentConfig = CustomScoreboard.alignmentConfig
- val backgroundConfig = CustomScoreboard.backgroundConfig
- val outlineConfig = backgroundConfig.outline
- val border = backgroundConfig.borderSize
-
- val x = position.getAbsX()
- val y = position.getAbsY()
+ private fun BackgroundConfig.createBackground(renderable: Renderable): Renderable =
+ if (backgroundConfig.useCustomBackgroundImage) {
+ Renderable.drawInsideImage(
+ renderable,
+ textureLocation,
+ (backgroundConfig.customBackgroundImageOpacity * 255) / 100,
+ borderSize,
+ horizontalAlign = RenderUtils.HorizontalAlignment.CENTER,
+ verticalAlign = RenderUtils.VerticalAlignment.CENTER,
+ radius = backgroundConfig.roundedCornerSmoothness,
+ )
+ } else {
+ Renderable.drawInsideRoundedRect(
+ renderable,
+ backgroundConfig.color.toChromaColor(),
+ borderSize,
+ backgroundConfig.roundedCornerSmoothness,
+ 1,
+ horizontalAlign = RenderUtils.HorizontalAlignment.CENTER,
+ verticalAlign = RenderUtils.VerticalAlignment.CENTER,
+ )
+ }
- val elementWidth = position.getDummySize().x
- val elementHeight = position.getDummySize().y
-
- val scaledWidth = ScaledResolution(Minecraft.getMinecraft()).scaledWidth
- val scaledHeight = ScaledResolution(Minecraft.getMinecraft()).scaledHeight
+ internal fun updatePosition(renderable: Renderable) {
+ if (GuiEditManager.isInGui()) return
+ val alignmentConfig = CustomScoreboard.alignmentConfig
- var newX = when (alignmentConfig.horizontalAlignment) {
- RenderUtils.HorizontalAlignment.LEFT -> border
- RenderUtils.HorizontalAlignment.CENTER -> scaledWidth / 2 - (elementWidth + border * 3) / 2
- RenderUtils.HorizontalAlignment.RIGHT -> scaledWidth - (elementWidth + border * 2)
- else -> x
+ with(alignmentConfig) {
+ if (horizontalAlignment == RenderUtils.HorizontalAlignment.DONT_ALIGN &&
+ verticalAlignment == RenderUtils.VerticalAlignment.DONT_ALIGN
+ ) return
}
- var newY = when (alignmentConfig.verticalAlignment) {
- RenderUtils.VerticalAlignment.TOP -> border
- RenderUtils.VerticalAlignment.CENTER -> scaledHeight / 2 - (elementHeight + border * 2) / 2
- RenderUtils.VerticalAlignment.BOTTOM -> scaledHeight - elementHeight - border
- else -> y
- }
+ val position = CustomScoreboard.config.position
- if (outlineConfig.enabled) {
- val thickness = outlineConfig.thickness
- if (alignmentConfig.horizontalAlignment == RenderUtils.HorizontalAlignment.RIGHT) {
- newX -= thickness / 2
- } else if (alignmentConfig.horizontalAlignment == RenderUtils.HorizontalAlignment.LEFT) {
- newX += thickness / 2
+ val scaledWidth = ScaledResolution(Minecraft.getMinecraft()).scaledWidth
+ val scaledHeight = ScaledResolution(Minecraft.getMinecraft()).scaledHeight
+ val elementWidth = (renderable.width * position.effectiveScale).toInt()
+ val elementHeight = (renderable.height * position.effectiveScale).toInt()
+
+ with(alignmentConfig) {
+ var x = when (horizontalAlignment) {
+ RenderUtils.HorizontalAlignment.DONT_ALIGN -> position.getAbsX()
+ RenderUtils.HorizontalAlignment.LEFT -> 0 + margin
+ RenderUtils.HorizontalAlignment.CENTER -> scaledWidth / 2 - elementWidth / 2
+ RenderUtils.HorizontalAlignment.RIGHT -> scaledWidth - elementWidth - margin
+ else -> 0
+ }
+ var y = when (verticalAlignment) {
+ RenderUtils.VerticalAlignment.DONT_ALIGN -> position.getAbsY()
+ RenderUtils.VerticalAlignment.TOP -> 0 + margin
+ RenderUtils.VerticalAlignment.CENTER -> scaledHeight / 2 - elementHeight / 2
+ RenderUtils.VerticalAlignment.BOTTOM -> scaledHeight - elementHeight - margin
+ else -> 0
}
- if (alignmentConfig.verticalAlignment == RenderUtils.VerticalAlignment.TOP) {
- newY += thickness / 2
- } else if (alignmentConfig.verticalAlignment == RenderUtils.VerticalAlignment.BOTTOM) {
- newY -= thickness / 2
+ val outlineConfig = backgroundConfig.outline
+ if (outlineConfig.enabled) {
+ val thickness = outlineConfig.thickness
+
+ when (horizontalAlignment) {
+ RenderUtils.HorizontalAlignment.RIGHT -> x -= thickness / 2
+ RenderUtils.HorizontalAlignment.LEFT -> x += thickness / 2
+ else -> x
+ }
+
+ when (verticalAlignment) {
+ RenderUtils.VerticalAlignment.TOP -> y += thickness / 2
+ RenderUtils.VerticalAlignment.BOTTOM -> y -= thickness / 2
+ else -> y
+ }
}
+ CustomScoreboard.config.position.moveTo(x, y)
}
-
- return Position(
- newX,
- newY,
- position.getScale(),
- position.isCenter,
- )
}
}
diff --git a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt
index 7c9757da2d0b..7636d43ebabf 100644
--- a/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt
+++ b/src/main/java/at/hannibal2/skyhanni/features/gui/customscoreboard/ScoreboardElements.kt
@@ -11,6 +11,7 @@ import at.hannibal2.skyhanni.data.IslandType
import at.hannibal2.skyhanni.data.MaxwellAPI
import at.hannibal2.skyhanni.data.MayorAPI
import at.hannibal2.skyhanni.data.MiningAPI
+import at.hannibal2.skyhanni.data.MiningAPI.inGlaciteArea
import at.hannibal2.skyhanni.data.PartyAPI
import at.hannibal2.skyhanni.data.PurseAPI
import at.hannibal2.skyhanni.data.QuiverAPI
@@ -52,6 +53,7 @@ import at.hannibal2.skyhanni.utils.RenderUtils.HorizontalAlignment
import at.hannibal2.skyhanni.utils.SkyBlockTime
import at.hannibal2.skyhanni.utils.StringUtils.firstLetterUppercase
import at.hannibal2.skyhanni.utils.StringUtils.pluralize
+import at.hannibal2.skyhanni.utils.TabListData
import at.hannibal2.skyhanni.utils.TimeLimitedSet
import at.hannibal2.skyhanni.utils.TimeUtils.format
import at.hannibal2.skyhanni.utils.TimeUtils.formatted
@@ -62,6 +64,8 @@ internal var unconfirmedUnknownLines = listOf()
internal var unknownLinesSet = TimeLimitedSet(1.seconds) { onRemoval(it) }
private fun onRemoval(line: String) {
+ if (!LorenzUtils.inSkyBlock) return
+ if (!unconfirmedUnknownLines.contains(line)) return
if (line !in unconfirmedUnknownLines) return
unconfirmedUnknownLines = unconfirmedUnknownLines.filterNot { it == line }
confirmedUnknownLines = confirmedUnknownLines.editCopy { add(line) }
@@ -74,7 +78,7 @@ private fun onRemoval(line: String) {
"Unknown Lines" to confirmedUnknownLines,
"Island" to LorenzUtils.skyBlockIsland,
"Area" to HypixelData.skyBlockArea,
- "Full Scoreboard" to ScoreboardData.sidebarLinesFormatted,
+ "Full Scoreboard" to CustomScoreboard.activeLines,
noStackTrace = true,
betaOnly = true,
)
@@ -147,9 +151,15 @@ enum class ScoreboardElement(
::shouldShowChunkedStats,
"§652,763,737 §7| §d64,647 §7| §6249M\n§b59,264 §7| §c23,495 §7| §a57,873\n§c♨ 0 §7| §b0❄ §7| §d756",
),
+ SOULFLOW(
+ ::getSoulflowDisplayPair,
+ ::getSoulflowDisplayWhen,
+ "Soulflow: §3761",
+ ),
EMPTY_LINE(
::getEmptyLineDisplayPair,
- { true }, "",
+ { true },
+ "",
),
ISLAND(
::getIslandDisplayPair,
@@ -203,7 +213,8 @@ enum class ScoreboardElement(
),
EMPTY_LINE2(
::getEmptyLineDisplayPair,
- { true }, "",
+ { true },
+ "",
),
OBJECTIVE(
::getObjectiveDisplayPair,
@@ -351,28 +362,35 @@ enum class ScoreboardElement(
}
}
-private fun getTitleDisplayPair(): List =
- if (displayConfig.titleAndFooter.useHypixelTitleAnimation) {
- listOf(ScoreboardData.objectiveTitle to displayConfig.titleAndFooter.alignTitleAndFooter)
- } else {
- listOf(
- displayConfig.titleAndFooter.customTitle.get().toString()
- .replace("&", "§")
- .split("\\n")
- .map { it to displayConfig.titleAndFooter.alignTitleAndFooter },
+private fun getTitleDisplayPair(): List {
+ val alignment = displayConfig.titleAndFooter.alignTitleAndFooter
+
+ if (!LorenzUtils.inSkyBlock && !displayConfig.titleAndFooter.useCustomTitleOutsideSkyBlock) {
+ return listOf(ScoreboardData.objectiveTitle to alignment)
+ }
+
+ return if (displayConfig.titleAndFooter.useCustomTitle) {
+ listOf(displayConfig.titleAndFooter.customTitle.get().toString()
+ .replace("&", "§")
+ .split("\\n")
+ .map { it to alignment }
).flatten()
+ } else {
+ listOf(ScoreboardData.objectiveTitle to alignment)
}
+}
-private fun getProfileDisplayPair() =
- listOf(CustomScoreboardUtils.getProfileTypeSymbol() + HypixelData.profileName.firstLetterUppercase() to HorizontalAlignment.LEFT)
+private fun getProfileDisplayPair() = listOf(
+ CustomScoreboardUtils.getProfileTypeSymbol() + HypixelData.profileName.firstLetterUppercase()
+ to HorizontalAlignment.LEFT,
+)
private fun getPurseDisplayPair(): List