aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer <spenceralj@gmail.com>2023-07-10 17:52:19 -0400
committerSpencer <spenceralj@gmail.com>2023-07-10 17:52:19 -0400
commit0f0f322d85c6b5ec40bdf3e569db67bf1252f4bc (patch)
tree4081363c9ecf4c4de324d8bf485b2766175d8d04
parent7a223d5a93b26a701911f7606d135296c1d5822c (diff)
parent4e5b4fb480339e303e0b31ab0a3a07c90c3912fc (diff)
downloadSkyblocker-0f0f322d85c6b5ec40bdf3e569db67bf1252f4bc.tar.gz
Skyblocker-0f0f322d85c6b5ec40bdf3e569db67bf1252f4bc.tar.bz2
Skyblocker-0f0f322d85c6b5ec40bdf3e569db67bf1252f4bc.zip
Fix merge conflicts
-rw-r--r--.github/workflows/buildrelease.yml44
-rw-r--r--CHANGELOG.md43
-rw-r--r--FEATURES.md19
-rw-r--r--README.md56
-rw-r--r--build.gradle12
-rw-r--r--gradle.properties22
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin61608 -> 62076 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rwxr-xr-xgradlew7
-rw-r--r--gradlew.bat184
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java104
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/chat/ChatMessageListener.java38
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/chat/filters/AdFilter.java46
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java192
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/container/ColorHighlight.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolver.java36
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/discord/DiscordRPCManager.java93
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/gui/ColorHighlight.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolver.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolverManager.java (renamed from src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolverManager.java)60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/ChatHudListenerMixin.java41
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java14
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java (renamed from src/main/java/me/xmrvizzy/skyblocker/mixin/ItemRendererMixin.java)33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java3
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenMixin.java31
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java92
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java18
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java57
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/ScreenMixin.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java16
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java15
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java175
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java58
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java62
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java17
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java2
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java32
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java57
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java4
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java21
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java8
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java15
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java73
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java11
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java128
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java59
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java81
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java80
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java399
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java31
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java68
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java143
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java31
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java17
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCategory.java97
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplay.java39
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplayGenerator.java67
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockerREIClientPlugin.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java79
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java27
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java88
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java21
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java16
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java230
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java51
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java48
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericRiftInfoScreen.java38
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java32
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java23
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java21
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/RiftScreen.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java29
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java160
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java92
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java91
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java48
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java45
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java99
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java55
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java64
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java103
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java24
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java68
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java79
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java53
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java78
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java44
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java37
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java60
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java134
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java73
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java25
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java68
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java28
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java75
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java22
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java47
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java57
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java203
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java32
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java45
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java30
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java33
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java69
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java58
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java27
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java55
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java19
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java97
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java26
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java41
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java20
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/FrustumUtils.java5
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/MessageScheduler.java63
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java101
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/RenderHelper.java86
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Scheduler.java96
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java66
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java182
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/title/Title.java53
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainer.java179
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainerConfigScreen.java164
-rw-r--r--src/main/resources/assets/skyblocker/lang/de_de.json7
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_ca.json11
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json75
-rw-r--r--src/main/resources/assets/skyblocker/lang/es_es.json148
-rw-r--r--src/main/resources/assets/skyblocker/lang/ja_jp.json (renamed from src/main/resources/assets/skyblocker/lang/ja_JP.json)0
-rw-r--r--src/main/resources/assets/skyblocker/lang/ko_kr.json (renamed from src/main/resources/assets/skyblocker/lang/ko_KR.json)0
-rw-r--r--src/main/resources/assets/skyblocker/lang/nb_no.json164
-rw-r--r--src/main/resources/assets/skyblocker/lang/nn_no.json16
-rw-r--r--src/main/resources/assets/skyblocker/lang/ru_ru.json143
-rw-r--r--src/main/resources/assets/skyblocker/lang/tr_tr.json71
-rw-r--r--src/main/resources/assets/skyblocker/lang/zh_cn.json162
-rw-r--r--src/main/resources/assets/skyblocker/lang/zh_tw.json24
-rw-r--r--src/main/resources/assets/skyblocker/mirrorverse_waypoints.json1019
-rw-r--r--src/main/resources/fabric.mod.json13
-rw-r--r--src/main/resources/skyblocker.mixins.json20
-rw-r--r--src/test/java/me/xmrvizzy/skyblocker/chat/filters/AdFilterTest.java18
-rw-r--r--src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java35
182 files changed, 9609 insertions, 1086 deletions
diff --git a/.github/workflows/buildrelease.yml b/.github/workflows/buildrelease.yml
index c9e3584b..d79c40e8 100644
--- a/.github/workflows/buildrelease.yml
+++ b/.github/workflows/buildrelease.yml
@@ -39,11 +39,51 @@ jobs:
id: read_changelog
shell: bash
run: |
- CHANGELOG=$(sed '/___/Q' CHANGELOG.md | grep -v '# ')
+ CHANGELOG=$(sed '/___/Q' CHANGELOG.md)
echo "Changelog:\n$CHANGELOG"
+
+ changelog="${CHANGELOG}"
+
+ # Because of the 2000 char limit in Discord shorten the changelog
+
+ # Calculate the number of characters to delete
+ delete_chars=$((${#changelog} + 350 - 2000 - 15))
+
+ # Check if delete_chars is greater than 0
+ if [ $delete_chars -gt 0 ]; then
+
+ # Extract the "What's Changed" section from the changelog
+ changed_section=$(echo "$changelog" | awk '/## What'\''s Changed/{flag=1;next}/^$/{flag=0}flag')
+
+ # Trim the changed_section based on the delete_chars value and remove the last line
+ modified_section="${changed_section::-delete_chars}"
+ modified_section=$(echo "$modified_section" | sed '$d')
+
+ # Add "[...] and more" at the end of modified_section
+ modified_section+="\n[...] and more"
+
+ # Format modified_section with printf
+ modified_section=$(printf "%s" "$modified_section")
+
+ # Generate the modified_changelog by inserting modified_section after the "What's Changed" section
+ modified_changelog=$(awk -v modified_section="$modified_section" '
+ /^## What'\''s Changed/ { print; print modified_section; f=1; next }
+ f && /^$/ { f=0 }
+ !f { print }
+ END { if (f) print "" }
+ ' ORS='\n' <<< "$changelog")
+
+ # Format the modified_changelog by removing "@" characters and enclosing URLs in "<>"
+ modified_changelog=$(echo "$modified_changelog" | sed -e 's/@//g' -e 's|https\?://[^[:space:]]*|<\0>|g')
+
+ # Store the modified_changelog in the CHANGELOG variable
+ CHANGELOG=$(echo -n "$modified_changelog")
+ fi
+
CHANGELOG="${CHANGELOG//'%'/'%25'}"
CHANGELOG="${CHANGELOG//$'\n'/'%0A'}"
CHANGELOG="${CHANGELOG//$'\r'/'%0D'}"
+
echo "::set-output name=changelog::$CHANGELOG"
- uses: actions/github-script@v2
@@ -53,11 +93,13 @@ jobs:
script: |
const fs = require("fs")
return fs.readdirSync("build/libs/").filter(e => !e.endsWith("dev.jar") && !e.endsWith("sources.jar") && e.endsWith(".jar"))[0]
+
- name: Release
id: uploadrelease
uses: softprops/action-gh-release@v1
with:
body: ${{ steps.read_changelog.outputs.changelog }}
+ token: ${{ secrets.GH_RELEASE }}
files: build/libs/${{ steps.fname.outputs.result }}
- name: Publish to Modrinth
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8aa8c4df..59b3fd68 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,46 @@
+# Release 1.10.0
+
+## Highlight
+* Custom Tab HUD by @msg-programs in https://github.com/SkyblockerMod/Skyblocker/pull/137
+* Fishing Helper by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/157
+* REI Compatibility by @lantice3720 in https://github.com/SkyblockerMod/Skyblocker/pull/148
+* Barn solvers by @Julienraptor01 in https://github.com/SkyblockerMod/Skyblocker/pull/134
+
+## What's Changed
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/142
+* Barn solvers by @Julienraptor01 in https://github.com/SkyblockerMod/Skyblocker/pull/134
+* Dungeon map scale adjustment customization by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/127
+* Add option to hide empty tooltips in inventories. by @msg-programs in https://github.com/SkyblockerMod/Skyblocker/pull/135
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/144
+* There is now 240 fairy souls! by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/138
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/149
+* minor updates by @Fix3dll in https://github.com/SkyblockerMod/Skyblocker/pull/122
+* Migrated to ClientReceiveMessageEvents and some fixes by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/131
+* Renamed container package to gui and added docs by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/145
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/153
+* REI Compatibility by @lantice3720 in https://github.com/SkyblockerMod/Skyblocker/pull/148
+* Api migration, config command, and cleanup by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/154
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/155
+* Refactors & docs by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/156
+* return instead of assign by @Fix3dll in https://github.com/SkyblockerMod/Skyblocker/pull/158
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/159
+* Add Fishing Helper by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/157
+* Update Loom and Gradle by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/163
+* Livid color by @kevinthegreat1 in https://github.com/SkyblockerMod/Skyblocker/pull/162
+* fix repository update by @Fix3dll in https://github.com/SkyblockerMod/Skyblocker/pull/165
+* remove backup LBIN server because it desn't exist anymore by @Julienraptor01 in https://github.com/SkyblockerMod/Skyblocker/pull/168
+* fixing missing strings by @PumpkinXD in https://github.com/SkyblockerMod/Skyblocker/pull/170
+* Translations update from hysky translate by @LifeIsAParadox in https://github.com/SkyblockerMod/Skyblocker/pull/166
+* Replace tab/playerlist HUD with a more fancy version by @msg-programs in https://github.com/SkyblockerMod/Skyblocker/pull/137
+* Add Dungeon Map Placement Screen by @AzureAaron in https://github.com/SkyblockerMod/Skyblocker/pull/169
+
+## New Contributors
+* @msg-programs made their first contribution in https://github.com/SkyblockerMod/Skyblocker/pull/135
+* @lantice3720 made their first contribution in https://github.com/SkyblockerMod/Skyblocker/pull/148
+* @PumpkinXD made their first contribution in https://github.com/SkyblockerMod/Skyblocker/pull/170
+
+**Full Changelog**: https://github.com/SkyblockerMod/Skyblocker/compare/v1.9.0...v1.10.0
+___
# Release 1.9.0
Supports 1.19.4
diff --git a/FEATURES.md b/FEATURES.md
index 1cd95a1e..3db3c2a5 100644
--- a/FEATURES.md
+++ b/FEATURES.md
@@ -2,14 +2,18 @@
* Hide Messages: Ability Cooldown, Heal, AOTE, Implosion, Molten Wave, Teleport Pad Messages
* Dungeon Minimap
* Dungeon Puzzle Solver:
- * Three Weirdos
- * Blaze
- * Croesus
- * F7 Terminal: Order, Color, Name
+ * Three Weirdos
+ * Blaze
+ * Croesus
+ * Terminal:
+ * Order
+ * Coloured Items
+ * Item Name
* Dwarven Mines Solver: Fetchur, Puzzler
+* Barn Solver: Treasure Hunter, Hungry Hiker
* Drill Fuel in Item Durability Bar
* Hotbar Slot Lock Keybind (Select the hotbar slot you want to lock/unlock and press the lockbutton)
-* price tooltip: npc, bazaar (avg, lbin), ah, museum
+* Price tooltip: npc, bazaar (avg, lbin), ah, museum
* reparty: write /rp to reparty
* Wiki Lookup: press f4 to open the wiki page about the held item
* Discord Rich Presence: Allows user to show either their Piggy, Bits, or location. Along with a custom message
@@ -20,4 +24,7 @@
hold shift to preview
* Update notification
* Commission HUD: Dwarven Mines quests
-* 1.8 hitbox for lever and farmland \ No newline at end of file
+* 1.8 hitbox for lever and farmland
+* Custom Tab HUD
+* Roughly enough items (REI) Support
+* Fishing Helper \ No newline at end of file
diff --git a/README.md b/README.md
index 59e8a7fa..9b5e323c 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[![Discord](https://img.shields.io/discord/879732108745125969?logo=discord&labelColor=cecece&color=7289DA&label=)](https://discord.com/invite/aNNJHQykck)
[![modrinth statistic](https://img.shields.io/badge/buy%20me%20coffee-skyblocker?color=434B57&logo=kofi)](https://ko-fi.com/wohlhabend)
-Hypixel Skyblock Mod for Minecraft 1.17.x + 1.18.x + 1.19.x
+Hypixel Skyblock Mod for Minecraft 1.17.x + 1.18.x + 1.19.x + 1.20.x
Installation guide is [here](https://github.com/SkyblockerMod/Skyblocker/wiki/installation)
@@ -21,14 +21,18 @@ Installation guide is [here](https://github.com/SkyblockerMod/Skyblocker/wiki/in
* Hide Messages: Ability Cooldown, Heal, AOTE, Implosion, Molten Wave, Teleport Pad Messages
* Dungeon Minimap
* Dungeon Puzzle Solver:
- * Three Weirdos
- * Blaze
- * Croesus
- * F7 Terminal: Order, Color, Name
+ * Three Weirdos
+ * Blaze
+ * Croesus
+ * Terminal:
+ * Order
+ * Coloured Items
+ * Item Name
* Dwarven Mines Solver: Fetchur, Puzzler
+* Barn Solver: Treasure Hunter, Hungry Hiker
* Drill Fuel in Item Durability Bar
* Hotbar Slot Lock Keybind (Select the hotbar slot you want to lock/unlock and press the lockbutton)
-* price tooltip: npc, bazaar (avg, lbin), ah, museum
+* Price tooltip: npc, bazaar (avg, lbin), ah, museum
* reparty: write /rp to reparty
* Wiki Lookup: press f4 to open the wiki page about the held item
* Discord Rich Presence: Allows user to show either their Piggy, Bits, or location. Along with a custom message
@@ -40,11 +44,22 @@ Installation guide is [here](https://github.com/SkyblockerMod/Skyblocker/wiki/in
* Update notification
* Commission HUD: Dwarven Mines quests
* 1.8 hitbox for lever and farmland
+* Custom Tab HUD
+* Roughly enough items (REI) Support
+* Fishing Helper
</details>
___
+## Commands
+| command | option | comment |
+|:----------------------------:|:--------------------:|:--------------------------------------|
+| /skyblocker config | | open config menu (modMenu not needed) |
+| /skyblocker options | | open config menu (modMenu not needed) |
+| /skyblocker hud | dwarven / dungeonmap | move dwarven or dungeonmap |
+
+---
## Images
<details open>
@@ -81,18 +96,29 @@ information.
| [Zailer43](https://github.com/Zailer43) | [TacoMonkey11](https://github.com/TacoMonkey11) | [KonaeAkira](https://github.com/KonaeAkira) | [Fix3dll](https://github.com/Fix3dll) |
| [<img alt="ADON15c" src="https://github.com/ADON15c.png" width="100">](https://github.com/ADON15c) | [<img alt="TheColdPot" src="https://github.com/TheColdPot.png" width="100">](https://github.com/TheColdPot) | [<img alt="Julienraptor01" src="https://github.com/Julienraptor01.png" width="100">](https://github.com/Julienraptor01) | [<img alt="MiraculixxT" src="https://github.com/MiraculixxT.png" width="100">](https://github.com/MiraculixxT) |
-|:--------------------------------------------------------------------------------------------------:|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
-| [ADON15c](https://github.com/ADON15c) | [TheColdPot](https://github.com/TheColdPot) | [Julienraptor01](https://github.com/Julienraptor01) | [ADON15c](https://github.com/MiraculixxT) |
+|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------:|
+| [ADON15c](https://github.com/ADON15c) | [TheColdPot](https://github.com/TheColdPot) | [Julienraptor01](https://github.com/Julienraptor01) | [MiraculixxT](https://github.com/MiraculixxT) |
+
+| [<img alt="catandA" src="https://github.com/catandA.png" width="100">](https://github.com/catandA) | [<img alt="kevinthegreat1" src="https://github.com/kevinthegreat1.png" width="100">](https://github.com/kevinthegreat1) | [<img alt="AzureAaron" src="https://github.com/AzureAaron.png" width="100">](https://github.com/AzureAaron) | [<img alt="msg-programs" src="https://github.com/msg-programs.png" width="100">](https://github.com/msg-programs) |
+|:--------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------:|:-----------------------------------------------------------------------------------------------------------------:|
+| [catandA](https://github.com/catandA) | [kevinthegreat1](https://github.com/kevinthegreat1) | [AzureAaron](https://github.com/AzureAaron) | [msg-programs](https://github.com/msg-programs) |
-| [<img alt="catandA" src="https://github.com/catandA.png" width="100">](https://github.com/catandA) | [<img alt="kevinthegreat1" src="https://github.com/kevinthegreat1.png" width="100">](https://github.com/kevinthegreat1) | [<img alt="AzureAaron" src="https://github.com/AzureAaron.png" width="100">](https://github.com/AzureAaron) |
-|:--------------------------------------------------------------------------------------------------:|-------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------|
-| [catandA](https://github.com/catandA) | [kevinthegreat1](https://github.com/kevinthegreat1) | [AzureAaron](https://github.com/AzureAaron) |
+| [<img alt="lantice3720" src="https://github.com/lantice3720.png" width="100">](https://github.com/lantice3720) |
+|:-----------------------------------------------------------------------------------------------------|
+| [lantice3720](https://github.com/lantice3720) |
### Translators
-German ([LifeIsAParadox](https://github.com/LifeIsAParadox)) \
+German ([LifeIsAParadox](https://github.com/LifeIsAParadox) & [msg-programs](https://github.com/msg-programs)) \
Indonesian ([null2264](https://github.com/null2264)) \
-Russian ([HyperSoop](https://github.com/HyperSoop)) \
+Russian ([HyperSoop](https://github.com/HyperSoop) & [Azuremane](https://github.com/Azuremane)) \
French ([edgarogh](https://github.com/edgarogh) & [Julienraptor01](https://github.com/Julienraptor01)) \
-Japanese ([hirochisan](https://github.com/@irochisan)) \
-Chinese ([catandA](https://github.com/catandA))
+Japanese ([hirochisan](https://github.com/hirochisan)) \
+Chinese ([catandA](https://github.com/catandA)) \
+Korean ([lantice3720](https://github.com/lantice3720)) \
+Spanish ([IngeSebastian](https://github.com/IngeSebastian)) \
+Norwegian Bokmål ([KdGaming0](https://github.com/KdGaming0)) \
+Norwegian Nynorsk ([KdGaming0](https://github.com/KdGaming0)) \
+Turkish ([Fix3dll](https://github.com/Fix3dll)) \
+Chinese (Simplified) ([catandA](https://github.com/catandA) & [PumpkinXD](https://github.com/PumpkinXD)) \
+Canadian English ([AzureAaron](https://github.com/AzureAaron)) \ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 37e4adea..fec3b902 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'fabric-loom' version '1.1-SNAPSHOT'
+ id 'fabric-loom' version '1.3-SNAPSHOT'
id 'maven-publish'
id 'com.modrinth.minotaur' version '2.+'
}
@@ -49,12 +49,19 @@ dependencies {
// Mod Menu
modImplementation "com.terraformersmc:modmenu:${project.mod_menu_version}"
+ // REI
+ modCompileOnly "me.shedaniel:RoughlyEnoughItems-api-fabric:${project.rei_version}"
+ modRuntimeOnly "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}"
+
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_api_version}"
// https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit used pull data from the NEU item repo
include(implementation("org.eclipse.jgit:org.eclipse.jgit:6.4.0.202211300538-r"))
+ // Renderer (https://github.com/0x3C50/Renderer)
+ include modImplementation("com.github.0x3C50:Renderer:${project.renderer_version}")
+
include(modImplementation ("meteordevelopment:discord-ipc:1.1"))
}
@@ -112,7 +119,8 @@ modrinth {
versionType = "release"
dependencies = [ // Yet another array. Create a new `ModDependency` or `VersionDependency` with two strings - the ID and the scope
new ModDependency("P7dR8mSH", "required"), // Creates a new required dependency on Fabric API
- new ModDependency("mOgUt4GM", "optional") // modmenu
+ new ModDependency("mOgUt4GM", "optional"), // modmenu
+ new ModDependency("nfn13YXA", "optional") // REI
]
changelog = System.getenv('CHANGELOG')
}
diff --git a/gradle.properties b/gradle.properties
index c883c1a2..cbb51b52 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,23 +1,27 @@
org.gradle.jvmargs=-Xmx1G -Dfile.encoding=UTF-8 -Duser.language=en -Duser.country=US
# Fabric Properties (https://fabricmc.net/versions.html)
-## 1.19.4
-minecraft_version=1.19.4
-yarn_mappings=1.19.4+build.1
-loader_version=0.14.18
+## 1.20
+minecraft_version=1.20
+yarn_mappings=1.20+build.1
+loader_version=0.14.21
#Fabric api
-## 1.19.4
-fabric_api_version=0.76.0+1.19.4
+## 1.20
+fabric_api_version=0.83.0+1.20
# Dependencies
## Cloth Api (https://www.curseforge.com/minecraft/mc-mods/cloth-config/files)
-clothconfig_version=10.0.96
+clothconfig_version=11.0.99
## Mod Menu (https://www.curseforge.com/minecraft/mc-mods/modmenu/files)
-mod_menu_version=6.1.0-rc.4
+mod_menu_version=7.0.1
+## REI (https://www.curseforge.com/minecraft/mc-mods/roughly-enough-items/files)
+rei_version=12.0.625
+## Renderer (https://github.com/0x3C50/Renderer)
+renderer_version = d687aced4c
# Mod Properties
-mod_version = 1.9.0
+mod_version = 1.10.0
maven_group = me.xmrvizzy
archives_base_name = skyblocker
modrinth_id=y6DuFGwJ \ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index ccebba77..c1962a79 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index bdc9a83b..37aef8d3 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 79a61d42..aeb74cbb 100755
--- a/gradlew
+++ b/gradlew
@@ -85,9 +85,6 @@ done
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
done
fi
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
diff --git a/gradlew.bat b/gradlew.bat
index 93e3f59f..6689b85b 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,92 +1,92 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java
deleted file mode 100644
index 1e6477db..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerInitializer.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package me.xmrvizzy.skyblocker;
-
-import me.xmrvizzy.skyblocker.chat.ChatMessageListener;
-import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.discord.DiscordRPCManager;
-import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock;
-import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
-import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
-import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup;
-import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
-import net.fabricmc.api.ClientModInitializer;
-
-public class SkyblockerInitializer implements ClientModInitializer {
- @Override
- public void onInitializeClient() {
- HotbarSlotLock.init();
- SkyblockerConfig.init();
- PriceInfoTooltip.init();
- WikiLookup.init();
- ItemRegistry.init();
- DwarvenHud.init();
- ChatMessageListener.init();
- DiscordRPCManager.init();
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
index f804d90e..4688d90f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
@@ -1,35 +1,109 @@
package me.xmrvizzy.skyblocker;
-import me.xmrvizzy.skyblocker.container.ContainerSolverManager;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import me.xmrvizzy.skyblocker.chat.ChatMessageListener;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.discord.DiscordRPCManager;
-import me.xmrvizzy.skyblocker.skyblock.BackpackPreview;
-import me.xmrvizzy.skyblocker.skyblock.StatusBarTracker;
+import me.xmrvizzy.skyblocker.gui.ContainerSolverManager;
+import me.xmrvizzy.skyblocker.skyblock.*;
+import me.xmrvizzy.skyblocker.skyblock.api.StatsCommand;
import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonBlaze;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
+import me.xmrvizzy.skyblocker.skyblock.dungeon.LividColor;
import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud;
-import me.xmrvizzy.skyblocker.utils.Scheduler;
-import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
+import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
+import me.xmrvizzy.skyblocker.skyblock.quicknav.QuickNav;
+import me.xmrvizzy.skyblocker.skyblock.rift.TheRift;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.utils.*;
+import me.xmrvizzy.skyblocker.utils.title.TitleContainer;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
+import net.fabricmc.loader.api.FabricLoader;
+import net.minecraft.client.MinecraftClient;
-public class SkyblockerMod {
+import java.nio.file.Path;
+
+/**
+ * Main class for Skyblocker which initializes features, registers events, and
+ * manages ticks. This class will be instantiated by Fabric. Do not instantiate
+ * this class.
+ */
+public class SkyblockerMod implements ClientModInitializer {
public static final String NAMESPACE = "skyblocker";
- private static final SkyblockerMod instance = new SkyblockerMod();
+ public static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir().resolve(NAMESPACE);
+ public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
+ private static SkyblockerMod INSTANCE;
+ @SuppressWarnings("deprecation")
public final Scheduler scheduler = new Scheduler();
+ public final MessageScheduler messageScheduler = new MessageScheduler();
public final ContainerSolverManager containerSolverManager = new ContainerSolverManager();
public final StatusBarTracker statusBarTracker = new StatusBarTracker();
- private SkyblockerMod() {
- scheduler.scheduleCyclic(Utils::sbChecker, 20);
- scheduler.scheduleCyclic(DiscordRPCManager::update, 100);
- scheduler.scheduleCyclic(DungeonBlaze::update, 4);
- scheduler.scheduleCyclic(BackpackPreview::tick, 50);
- scheduler.scheduleCyclic(DwarvenHud::update, 40);
+ /**
+ * Do not instantiate this class. Use {@link #getInstance()} instead.
+ */
+ @Deprecated
+ public SkyblockerMod() {
+ INSTANCE = this;
}
public static SkyblockerMod getInstance() {
- return instance;
+ return INSTANCE;
+ }
+
+ /**
+ * Register {@link #tick(MinecraftClient)} to
+ * {@link ClientTickEvents#END_CLIENT_TICK}, initialize all features, and
+ * schedule tick events.
+ */
+ @Override
+ public void onInitializeClient() {
+ ClientTickEvents.END_CLIENT_TICK.register(this::tick);
+ Utils.init();
+ HotbarSlotLock.init();
+ SkyblockerConfig.init();
+ PriceInfoTooltip.init();
+ WikiLookup.init();
+ ItemRegistry.init();
+ NEURepo.init();
+ BackpackPreview.init();
+ QuickNav.init();
+ StatsCommand.init();
+ DwarvenHud.init();
+ ChatMessageListener.init();
+ UpdateChecker.init();
+ DiscordRPCManager.init();
+ LividColor.init();
+ FishingHelper.init();
+ FairySouls.init();
+ TabHud.init();
+ DungeonMap.init();
+ TheRift.init();
+ TitleContainer.init();
+ containerSolverManager.init();
+ scheduler.scheduleCyclic(Utils::update, 20);
+ scheduler.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100);
+ scheduler.scheduleCyclic(DungeonBlaze::update, 4);
+ scheduler.scheduleCyclic(LividColor::update, 10);
+ scheduler.scheduleCyclic(BackpackPreview::tick, 50);
+ scheduler.scheduleCyclic(DwarvenHud::update, 40);
+ scheduler.scheduleCyclic(PlayerListMgr::updateList, 20);
}
- public void onTick() {
+ /**
+ * Ticks the scheduler. Called once at the end of every client tick through
+ * {@link ClientTickEvents#END_CLIENT_TICK}.
+ *
+ * @param client the Minecraft client.
+ */
+ public void tick(MinecraftClient client) {
scheduler.tick();
+ messageScheduler.tick();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/chat/ChatMessageListener.java b/src/main/java/me/xmrvizzy/skyblocker/chat/ChatMessageListener.java
index a32123e0..d58d03aa 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/chat/ChatMessageListener.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/chat/ChatMessageListener.java
@@ -8,11 +8,19 @@ import me.xmrvizzy.skyblocker.skyblock.dungeon.ThreeWeirdos;
import me.xmrvizzy.skyblocker.skyblock.dungeon.Trivia;
import me.xmrvizzy.skyblocker.skyblock.dwarven.Fetchur;
import me.xmrvizzy.skyblocker.skyblock.dwarven.Puzzler;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.text.Text;
+@FunctionalInterface
public interface ChatMessageListener {
+ /**
+ * An event called when a game message is received. Register your listeners in {@link ChatMessageListener#init()}.
+ */
Event<ChatMessageListener> EVENT = EventFactory.createArrayBacked(ChatMessageListener.class,
(listeners) -> (message, asString) -> {
for (ChatMessageListener listener : listeners) {
@@ -22,6 +30,9 @@ public interface ChatMessageListener {
return ChatFilterResult.PASS;
});
+ /**
+ * Registers {@link ChatMessageListener}s to {@link ChatMessageListener#EVENT} and registers {@link ChatMessageListener#EVENT} to {@link ClientReceiveMessageEvents#ALLOW_GAME}
+ */
static void init() {
ChatMessageListener[] listeners = new ChatMessageListener[]{
// Features
@@ -43,8 +54,33 @@ public interface ChatMessageListener {
new TeleportPadFilter(),
new AutopetFilter(),
};
- for (ChatMessageListener listener : listeners)
+ // Register all listeners to EVENT
+ for (ChatMessageListener listener : listeners) {
EVENT.register(listener);
+ }
+ // Register EVENT to ClientReceiveMessageEvents.ALLOW_GAME from fabric api
+ ClientReceiveMessageEvents.ALLOW_GAME.register((message, overlay) -> {
+ if (!Utils.isOnSkyblock()) {
+ return true;
+ }
+ ChatFilterResult result = EVENT.invoker().onMessage(message, message.getString());
+ switch (result) {
+ case ACTION_BAR -> {
+ if (overlay) {
+ return true;
+ }
+ ClientPlayerEntity player = MinecraftClient.getInstance().player;
+ if (player != null) {
+ player.sendMessage(message, true);
+ return false;
+ }
+ }
+ case FILTER -> {
+ return false;
+ }
+ }
+ return true;
+ });
}
ChatFilterResult onMessage(Text message, String asString);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/chat/filters/AdFilter.java b/src/main/java/me/xmrvizzy/skyblocker/chat/filters/AdFilter.java
index 5f9f463d..67734438 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/chat/filters/AdFilter.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/chat/filters/AdFilter.java
@@ -9,30 +9,30 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AdFilter extends ChatPatternListener {
- private static final Pattern[] AD_FILTERS = new Pattern[]{
- Pattern.compile("^(?:i(?:m|'m| am)? |(?:is )?any(?: ?one|1) )?(?:buy|sell|lowball|trade?)(?:ing)?(?:\\W|$)", Pattern.CASE_INSENSITIVE),
- Pattern.compile("(.)\\1{7,}"),
- Pattern.compile("\\W(?:on|in|check|at) my (?:ah|bin)(?:\\W|$)", Pattern.CASE_INSENSITIVE),
- };
+ private static final Pattern[] AD_FILTERS = new Pattern[] {
+ Pattern.compile("^(?:i(?:m|'m| am)? |(?:is )?any(?: ?one|1) )?(?:buy|sell|lowball|trade?)(?:ing)?(?:\\W|$)", Pattern.CASE_INSENSITIVE),
+ Pattern.compile("(.)\\1{7,}"),
+ Pattern.compile("\\W(?:on|in|check|at) my (?:ah|bin)(?:\\W|$)", Pattern.CASE_INSENSITIVE), };
- public AdFilter() {
- // Groups:
- // 1. Player name
- // 2. Message
- super("^§[67ab](?:\\[(?:MVP|VIP)(?:§[0-9a-f]\\+{1,2}§[6ab])?] )?([a-zA-Z0-9_]{2,16})§[7f]: (.*)$");
- }
+ public AdFilter() {
+ // Groups:
+ // 1. Player name
+ // 2. Message
+ // (?:§8\[[§fadbc0-9]+§8\] )?§[67abc](?:\[[§A-Za-z0-9+]+\] )?([A-Za-z0-9_]+)§[f7]: (.+)
+ super("(?:§8\\[[§fadbc0-9]+§8\\] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)§[f7]: (.+)");
+ }
- @Override
- public boolean onMatch(Text _message, Matcher matcher) {
- String message = matcher.group(2);
- for (Pattern adFilter : AD_FILTERS)
- if (adFilter.matcher(message).find())
- return true;
- return false;
- }
+ @Override
+ public boolean onMatch(Text _message, Matcher matcher) {
+ String message = matcher.group(2);
+ for (Pattern adFilter : AD_FILTERS)
+ if (adFilter.matcher(message).find())
+ return true;
+ return false;
+ }
- @Override
- protected ChatFilterResult state() {
- return SkyblockerConfig.get().messages.hideAds;
- }
+ @Override
+ protected ChatFilterResult state() {
+ return SkyblockerConfig.get().messages.hideAds;
+ }
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
index 48eb31c7..7da9979a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java
@@ -1,16 +1,23 @@
package me.xmrvizzy.skyblocker.config;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import me.shedaniel.autoconfig.AutoConfig;
import me.shedaniel.autoconfig.ConfigData;
import me.shedaniel.autoconfig.annotation.Config;
import me.shedaniel.autoconfig.annotation.ConfigEntry;
import me.shedaniel.autoconfig.serializer.GsonConfigSerializer;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.chat.ChatFilterResult;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.resource.language.I18n;
import java.util.ArrayList;
import java.util.List;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
@Config(name = "skyblocker")
public class SkyblockerConfig implements ConfigData {
@@ -22,6 +29,10 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Gui.TransitiveObject
public Locations locations = new Locations();
+ @ConfigEntry.Category("slayer")
+ @ConfigEntry.Gui.TransitiveObject
+ public Slayer slayer = new Slayer();
+
@ConfigEntry.Category("quickNav")
@ConfigEntry.Gui.TransitiveObject
public QuickNav quickNav = new QuickNav();
@@ -59,7 +70,7 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("button6")
@ConfigEntry.Gui.CollapsibleObject()
- public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "Storage", "/storage");
+ public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), "Storage", "/storage");
@ConfigEntry.Category("button7")
@ConfigEntry.Gui.CollapsibleObject()
@@ -124,16 +135,33 @@ public class SkyblockerConfig implements ConfigData {
public static class General {
public boolean enableUpdateNotification = true;
+ public boolean acceptReparty = true;
public boolean backpackPreviewWithoutShift = false;
public boolean hideEmptyTooltips = true;
+ @ConfigEntry.Category("tabHud")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public TabHudConf tabHud = new TabHudConf();
+
@ConfigEntry.Gui.Excluded
public String apiKey;
@ConfigEntry.Category("bars")
@ConfigEntry.Gui.CollapsibleObject()
public Bars bars = new Bars();
-
+
+ @ConfigEntry.Category("experiments")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public Experiments experiments = new Experiments();
+
+ @ConfigEntry.Category("fishing")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public Fishing fishing = new Fishing();
+
+ @ConfigEntry.Category("fairySouls")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public FairySouls fairySouls = new FairySouls();
+
@ConfigEntry.Category("itemList")
@ConfigEntry.Gui.CollapsibleObject()
public ItemList itemList = new ItemList();
@@ -146,10 +174,23 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Gui.CollapsibleObject()
public Hitbox hitbox = new Hitbox();
+ @ConfigEntry.Gui.Tooltip()
+ @ConfigEntry.Category("titleContainer")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public TitleContainer titleContainer = new TitleContainer();
+
@ConfigEntry.Gui.Excluded
public List<Integer> lockedSlots = new ArrayList<>();
}
+ public static class TabHudConf {
+ public boolean tabHudEnabled = true;
+
+ @ConfigEntry.BoundedDiscrete(min = 10, max = 200)
+ @ConfigEntry.Gui.Tooltip()
+ public int tabHudScale = 100;
+ }
+
public static class Bars {
public boolean enableBars = true;
@@ -177,7 +218,7 @@ public class SkyblockerConfig implements ConfigData {
NONE;
@Override
- public String toString() {
+ public String toString() {
return I18n.translate("text.autoconfig.skyblocker.option.general.bars.barpositions." + name());
}
@@ -191,11 +232,64 @@ public class SkyblockerConfig implements ConfigData {
}
}
+ public static class Experiments {
+ public boolean enableChronomatronSolver = true;
+ public boolean enableSuperpairsSolver = true;
+ public boolean enableUltrasequencerSolver = true;
+ }
+
+ public static class Fishing {
+ public boolean enableFishingHelper = true;
+ }
+
+ public static class FairySouls {
+ public boolean enableFairySoulsHelper = false;
+ }
+
public static class Hitbox {
public boolean oldFarmlandHitbox = true;
public boolean oldLeverHitbox = false;
}
+ public static class TitleContainer {
+ @ConfigEntry.BoundedDiscrete(min = 30, max = 140)
+ public float titleContainerScale = 100;
+ public int x = 540;
+ public int y = 10;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
+ public Direction direction = Direction.HORIZONTAL;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.DROPDOWN)
+ public Alignment alignment = Alignment.MIDDLE;
+ }
+
+ public enum Direction {
+ HORIZONTAL,
+ VERTICAL;
+
+ @Override
+ public String toString() {
+ return switch (this) {
+ case HORIZONTAL -> "Horizontal";
+ case VERTICAL -> "Vertical";
+ };
+ }
+ }
+
+ public enum Alignment {
+ LEFT,
+ RIGHT,
+ MIDDLE;
+
+ @Override
+ public String toString() {
+ return switch (this) {
+ case LEFT -> "Left";
+ case RIGHT -> "Right";
+ case MIDDLE -> "Middle";
+ };
+ }
+ }
+
public static class RichPresence {
public boolean enableRichPresence = false;
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
@@ -215,13 +309,15 @@ public class SkyblockerConfig implements ConfigData {
BOTH;
@Override
- public String toString() {
+ public String toString() {
return I18n.translate("text.autoconfig.skyblocker.option.general.itemTooltip.avg." + name());
}
}
public static class ItemTooltip {
public boolean enableNPCPrice = true;
+ @ConfigEntry.Gui.Tooltip
+ public boolean enableMotesPrice = true;
public boolean enableAvgBIN = true;
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
@ConfigEntry.Gui.Tooltip()
@@ -243,6 +339,10 @@ public class SkyblockerConfig implements ConfigData {
@ConfigEntry.Category("dwarvenmines")
@ConfigEntry.Gui.CollapsibleObject()
public DwarvenMines dwarvenMines = new DwarvenMines();
+
+ @ConfigEntry.Category("rift")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public Rift rift = new Rift();
}
public static class Dungeons {
@@ -250,13 +350,24 @@ public class SkyblockerConfig implements ConfigData {
public boolean croesusHelper = true;
public boolean enableMap = true;
public float mapScaling = 1f;
+ public int mapX = 2;
+ public int mapY = 2;
public boolean solveThreeWeirdos = true;
public boolean blazesolver = true;
public boolean solveTrivia = true;
+ @ConfigEntry.Gui.CollapsibleObject
+ public LividColor lividColor = new LividColor();
@ConfigEntry.Gui.CollapsibleObject()
public Terminals terminals = new Terminals();
}
+ public static class LividColor {
+ @ConfigEntry.Gui.Tooltip()
+ public boolean enableLividColor = true;
+ @ConfigEntry.Gui.Tooltip()
+ public String lividColorText = "The livid color is [color]";
+ }
+
public static class Terminals {
public boolean solveColor = true;
public boolean solveOrder = true;
@@ -273,16 +384,70 @@ public class SkyblockerConfig implements ConfigData {
public static class DwarvenHud {
public boolean enabled = true;
+ @ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
+ @ConfigEntry.Gui.Tooltip(count = 3)
+ public Style style = Style.SIMPLE;
public boolean enableBackground = true;
public int x = 10;
public int y = 10;
}
+ public enum Style {
+ SIMPLE,
+ FANCY,
+ CLASSIC;
+
+ @Override
+ public String toString() {
+ return switch (this) {
+ case SIMPLE -> "Simple";
+ case FANCY -> "Fancy";
+ case CLASSIC -> "Classic";
+ };
+ }
+ }
+
public static class Barn {
public boolean solveHungryHiker = true;
public boolean solveTreasureHunter = true;
}
+ public static class Rift {
+ public boolean mirrorverseWaypoints = true;
+ @ConfigEntry.BoundedDiscrete(min = 0, max = 5)
+ @ConfigEntry.Gui.Tooltip
+ public int mcGrubberStacks = 0;
+ }
+
+ public static class Slayer {
+ @ConfigEntry.Category("vampire")
+ @ConfigEntry.Gui.CollapsibleObject()
+ public VampireSlayer vampireSlayer = new VampireSlayer();
+ }
+
+ public static class VampireSlayer {
+ public boolean enableEffigyWaypoints = true;
+ public boolean compactEffigyWaypoints;
+ @ConfigEntry.BoundedDiscrete(min = 1, max = 10)
+ @ConfigEntry.Gui.Tooltip()
+ public int effigyUpdateFrequency = 5;
+ public boolean enableHolyIceIndicator = true;
+ public int holyIceIndicatorTickDelay = 10;
+ @ConfigEntry.BoundedDiscrete(min = 1, max = 10)
+ @ConfigEntry.Gui.Tooltip()
+ public int holyIceUpdateFrequency = 5;
+ public boolean enableHealingMelonIndicator = true;
+ public float healingMelonHealthThreshold = 4F;
+ public boolean enableSteakStakeIndicator = true;
+ @ConfigEntry.BoundedDiscrete(min = 1, max = 10)
+ @ConfigEntry.Gui.Tooltip()
+ public int steakStakeUpdateFrequency = 5;
+ public boolean enableManiaIndicator = true;
+ @ConfigEntry.BoundedDiscrete(min = 1, max = 10)
+ @ConfigEntry.Gui.Tooltip()
+ public int maniaUpdateFrequency = 5;
+ }
+
public static class Messages {
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
public ChatFilterResult hideAbility = ChatFilterResult.PASS;
@@ -310,14 +475,33 @@ public class SkyblockerConfig implements ConfigData {
PURSE,
BITS,
LOCATION;
+
@Override
public String toString() {
return I18n.translate("text.autoconfig.skyblocker.option.richPresence.info." + name());
}
}
+ /**
+ * Registers the config to AutoConfig and register commands to open the config screen.
+ */
public static void init() {
AutoConfig.register(SkyblockerConfig.class, GsonConfigSerializer::new);
+ ClientCommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess) -> dispatcher.register(literal("skyblocker").then(optionsLiteral("config")).then(optionsLiteral("options")))));
+ }
+
+ /**
+ * Registers an options command with the given name. Used for registering both options and config as valid commands.
+ *
+ * @param name the name of the command node
+ * @return the command builder
+ */
+ private static LiteralArgumentBuilder<FabricClientCommandSource> optionsLiteral(String name) {
+ return literal(name).executes(context -> {
+ // Don't immediately open the next screen as it will be closed by ChatScreen right after this command is executed
+ SkyblockerMod.getInstance().scheduler.queueOpenScreen(AutoConfig.getConfigScreen(SkyblockerConfig.class, null));
+ return Command.SINGLE_SUCCESS;
+ });
}
public static SkyblockerConfig get() {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/container/ColorHighlight.java b/src/main/java/me/xmrvizzy/skyblocker/container/ColorHighlight.java
deleted file mode 100644
index c4380eab..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/container/ColorHighlight.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package me.xmrvizzy.skyblocker.container;
-
-public record ColorHighlight(int slot, int color) {
-} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolver.java b/src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolver.java
deleted file mode 100644
index ec086934..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolver.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package me.xmrvizzy.skyblocker.container;
-
-import net.minecraft.item.ItemStack;
-
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-public abstract class ContainerSolver {
- private final Pattern CONTAINER_NAME;
- protected final static int GREEN_HIGHLIGHT = 128 << 24 | 64 << 16 | 196 << 8 | 64;
- protected final static int GRAY_HIGHLIGHT = 128 << 24 | 64 << 16 | 64 << 8 | 64;
-
- public ContainerSolver(String containerName) {
- CONTAINER_NAME = Pattern.compile(containerName);
- }
-
- public abstract boolean isEnabled();
-
- public Pattern getName() {
- return CONTAINER_NAME;
- }
-
- public abstract List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots);
-
- public void trimEdges(Map<Integer, ItemStack> slots, int rows) {
- for (int i = 0; i < rows; i++) {
- slots.remove(9 * i);
- slots.remove(9 * i + 8);
- }
- for (int i = 1; i < 8; i++) {
- slots.remove(i);
- slots.remove((rows - 1) * 9 + i);
- }
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/discord/DiscordRPCManager.java b/src/main/java/me/xmrvizzy/skyblocker/discord/DiscordRPCManager.java
index 10b88ce8..325f271a 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/discord/DiscordRPCManager.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/discord/DiscordRPCManager.java
@@ -11,42 +11,89 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
+import java.util.concurrent.CompletableFuture;
+/**
+ * Manages the discord rich presence. Automatically connects to discord and displays a customizable activity when playing Skyblock.
+ */
public class DiscordRPCManager {
public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###,###.##");
public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Discord RPC");
+ /**
+ * The update task used to avoid multiple update tasks running simultaneously.
+ */
+ public static CompletableFuture<Void> updateTask;
public static long startTimeStamp;
public static int cycleCount;
- public static void init(){
- SkyblockEvents.LEAVE.register(DiscordIPC::stop);
+ public static void init() {
+ SkyblockEvents.LEAVE.register(DiscordRPCManager::initAndUpdatePresence);
SkyblockEvents.JOIN.register(() -> {
startTimeStamp = System.currentTimeMillis();
- if (DiscordIPC.start(934607927837356052L, null)) {
- DiscordIPC.setActivity(buildPresence());
- LOGGER.info("Discord RPC started");
- } else {
- LOGGER.error("Discord RPC failed to start");
- }
+ initAndUpdatePresence(true);
});
}
- public static void update(){
+ /**
+ * Checks the {@link SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence.
+ */
+ public static void updateDataAndPresence() {
// If the custom message is empty, discord will keep the last message, this is can serve as a default if the user doesn't want a custom message
if (SkyblockerConfig.get().richPresence.customMessage.isEmpty()) {
SkyblockerConfig.get().richPresence.customMessage = "Playing Skyblock";
AutoConfig.getConfigHolder(SkyblockerConfig.class).save();
}
- if ((!Utils.isOnSkyblock || !SkyblockerConfig.get().richPresence.enableRichPresence) && DiscordIPC.isConnected()){
- DiscordIPC.stop();
- LOGGER.info("Discord RPC stopped");
- return;
- }
if (SkyblockerConfig.get().richPresence.cycleMode) cycleCount = (cycleCount + 1) % 3;
- DiscordIPC.setActivity(buildPresence());
+ initAndUpdatePresence();
+ }
+
+ /**
+ * @see #initAndUpdatePresence(boolean)
+ */
+ private static void initAndUpdatePresence() {
+ initAndUpdatePresence(false);
+ }
+
+ /**
+ * Updates discord presence asynchronously.
+ * <p>
+ * When the {@link #updateTask previous update} does not exist or {@link CompletableFuture#isDone() has completed}:
+ * <p>
+ * Connects to discord if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled},
+ * the player {@link Utils#isOnSkyblock() is on Skyblock}, and {@link DiscordIPC#isConnected() discord is not already connected}.
+ * Updates the presence if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}
+ * and the player {@link Utils#isOnSkyblock() is on Skyblock}.
+ * Stops the connection if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}
+ * or the player {@link Utils#isOnSkyblock() is not on Skyblock} and {@link DiscordIPC#isConnected() discord is connected}.
+ * Saves the update task in {@link #updateTask}
+ *
+ * @param initialization whether this is the first time the presence is being updates. If {@code true}, a message will be logged
+ * if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}.
+ */
+ private static void initAndUpdatePresence(boolean initialization) {
+ if (updateTask == null || updateTask.isDone()) {
+ updateTask = CompletableFuture.runAsync(() -> {
+ if (SkyblockerConfig.get().richPresence.enableRichPresence && Utils.isOnSkyblock()) {
+ if (!DiscordIPC.isConnected()) {
+ if (DiscordIPC.start(934607927837356052L, null)) {
+ LOGGER.info("Discord RPC started successfully");
+ } else {
+ LOGGER.error("Discord RPC failed to start");
+ return;
+ }
+ }
+ DiscordIPC.setActivity(buildPresence());
+ } else if (DiscordIPC.isConnected()) {
+ DiscordIPC.stop();
+ LOGGER.info("Discord RPC stopped");
+ } else if (initialization) {
+ LOGGER.info("Discord RPC is currently disabled");
+ }
+ });
+ }
}
- public static RichPresence buildPresence(){
+ public static RichPresence buildPresence() {
RichPresence presence = new RichPresence();
presence.setLargeImage("skyblocker-default", null);
presence.setStart(startTimeStamp);
@@ -55,19 +102,19 @@ public class DiscordRPCManager {
return presence;
}
- public static String getInfo(){
+ public static String getInfo() {
String info = null;
- if (!SkyblockerConfig.get().richPresence.cycleMode){
- switch (SkyblockerConfig.get().richPresence.info){
+ if (!SkyblockerConfig.get().richPresence.cycleMode) {
+ switch (SkyblockerConfig.get().richPresence.info) {
case BITS -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits());
case PURSE -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse());
- case LOCATION -> info = "⏣ " + Utils.getLocation();
+ case LOCATION -> info = Utils.getLocation();
}
- } else if (SkyblockerConfig.get().richPresence.cycleMode){
- switch (cycleCount){
+ } else if (SkyblockerConfig.get().richPresence.cycleMode) {
+ switch (cycleCount) {
case 0 -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits());
case 1 -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse());
- case 2 -> info = "⏣ " + Utils.getLocation();
+ case 2 -> info = Utils.getLocation();
}
}
return info;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/gui/ColorHighlight.java b/src/main/java/me/xmrvizzy/skyblocker/gui/ColorHighlight.java
new file mode 100644
index 00000000..4367e6e7
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/gui/ColorHighlight.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.gui;
+
+public record ColorHighlight(int slot, int color) {
+ private static final int RED_HIGHLIGHT = 64 << 24 | 255 << 16;
+ private static final int YELLOW_HIGHLIGHT = 128 << 24 | 255 << 16 | 255 << 8;
+ private static final int GREEN_HIGHLIGHT = 128 << 24 | 64 << 16 | 196 << 8 | 64;
+ private static final int GRAY_HIGHLIGHT = 128 << 24 | 64 << 16 | 64 << 8 | 64;
+
+ public static ColorHighlight red(int slot) {
+ return new ColorHighlight(slot, RED_HIGHLIGHT);
+ }
+
+ public static ColorHighlight yellow(int slot) {
+ return new ColorHighlight(slot, YELLOW_HIGHLIGHT);
+ }
+
+ public static ColorHighlight green(int slot) {
+ return new ColorHighlight(slot, GREEN_HIGHLIGHT);
+ }
+
+ public static ColorHighlight gray(int slot) {
+ return new ColorHighlight(slot, GRAY_HIGHLIGHT);
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolver.java b/src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolver.java
new file mode 100644
index 00000000..c5e3cf09
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolver.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.gui;
+
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.item.ItemStack;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Abstract class for gui solvers. Extend this class to add a new gui solver, like terminal solvers or experiment solvers.
+ */
+public abstract class ContainerSolver {
+ private final Pattern containerName;
+
+ protected ContainerSolver(String containerName) {
+ this.containerName = Pattern.compile(containerName);
+ }
+
+ protected abstract boolean isEnabled();
+
+ public Pattern getName() {
+ return containerName;
+ }
+
+ protected void start(GenericContainerScreen screen) {
+ }
+
+ protected void reset() {
+ }
+
+ protected abstract List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots);
+
+ protected void trimEdges(Map<Integer, ItemStack> slots, int rows) {
+ for (int i = 0; i < rows; i++) {
+ slots.remove(9 * i);
+ slots.remove(9 * i + 8);
+ }
+ for (int i = 1; i < 8; i++) {
+ slots.remove(i);
+ slots.remove((rows - 1) * 9 + i);
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolverManager.java b/src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolverManager.java
index 276ac3b9..0c27704d 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/container/ContainerSolverManager.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/gui/ContainerSolverManager.java
@@ -1,11 +1,17 @@
-package me.xmrvizzy.skyblocker.container;
+package me.xmrvizzy.skyblocker.gui;
import com.mojang.blaze3d.systems.RenderSystem;
+import me.xmrvizzy.skyblocker.mixin.HandledScreenAccessor;
import me.xmrvizzy.skyblocker.skyblock.dungeon.CroesusHelper;
import me.xmrvizzy.skyblocker.skyblock.dungeon.terminal.ColorTerminal;
import me.xmrvizzy.skyblocker.skyblock.dungeon.terminal.OrderTerminal;
import me.xmrvizzy.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal;
-import net.minecraft.client.gui.DrawableHelper;
+import me.xmrvizzy.skyblocker.skyblock.experiment.ChronomatronSolver;
+import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver;
+import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
@@ -18,7 +24,10 @@ import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class ContainerSolverManager extends DrawableHelper {
+/**
+ * Manager class for {@link ContainerSolver}s like terminal solvers and experiment solvers. To add a new gui solver, extend {@link ContainerSolver} and register it in {@link #ContainerSolverManager()}.
+ */
+public class ContainerSolverManager {
private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile("");
private final ContainerSolver[] solvers;
private ContainerSolver currentSolver = null;
@@ -30,10 +39,35 @@ public class ContainerSolverManager extends DrawableHelper {
new ColorTerminal(),
new OrderTerminal(),
new StartsWithTerminal(),
- new CroesusHelper()
+ new CroesusHelper(),
+ new ChronomatronSolver(),
+ new SuperpairsSolver(),
+ new UltrasequencerSolver()
};
}
+ public ContainerSolver getCurrentSolver() {
+ return currentSolver;
+ }
+
+ public void init() {
+ ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) {
+ ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, delta) -> {
+ MatrixStack matrices = context.getMatrices();
+ matrices.push();
+ matrices.translate(((HandledScreenAccessor) genericContainerScreen).getX(), ((HandledScreenAccessor) genericContainerScreen).getY(), 300);
+ onDraw(context, genericContainerScreen.getScreenHandler().slots.subList(0, genericContainerScreen.getScreenHandler().getRows() * 9));
+ matrices.pop();
+ });
+ ScreenEvents.remove(screen).register(screen1 -> clearScreen());
+ onSetScreen(genericContainerScreen);
+ } else {
+ clearScreen();
+ }
+ });
+ }
+
public void onSetScreen(@NotNull GenericContainerScreen screen) {
String screenName = screen.getTitle().getString();
Matcher matcher = PLACEHOLDER_PATTERN.matcher(screenName);
@@ -44,37 +78,41 @@ public class ContainerSolverManager extends DrawableHelper {
if (matcher.matches()) {
currentSolver = solver;
groups = new String[matcher.groupCount()];
- for (int i = 0; i < groups.length; i++)
+ for (int i = 0; i < groups.length; i++) {
groups[i] = matcher.group(i + 1);
+ }
+ currentSolver.start(screen);
return;
}
}
}
- currentSolver = null;
+ clearScreen();
}
public void clearScreen() {
- currentSolver = null;
+ if (currentSolver != null) {
+ currentSolver.reset();
+ currentSolver = null;
+ }
}
public void markDirty() {
highlights = null;
}
- public void onDraw(MatrixStack matrices, List<Slot> slots) {
+ public void onDraw(DrawContext context, List<Slot> slots) {
if (currentSolver == null)
return;
if (highlights == null)
highlights = currentSolver.getColors(groups, slotMap(slots));
- RenderSystem.disableDepthTest();
+ RenderSystem.enableDepthTest();
RenderSystem.colorMask(true, true, true, false);
for (ColorHighlight highlight : highlights) {
Slot slot = slots.get(highlight.slot());
int color = highlight.color();
- fillGradient(matrices, slot.x, slot.y, slot.x + 16, slot.y + 16, color, color);
+ context.fillGradient(slot.x, slot.y, slot.x + 16, slot.y + 16, color, color);
}
RenderSystem.colorMask(true, true, true, true);
- RenderSystem.enableDepthTest();
}
private Map<Integer, ItemStack> slotMap(List<Slot> slots) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ChatHudListenerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ChatHudListenerMixin.java
deleted file mode 100644
index 8176a810..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ChatHudListenerMixin.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package me.xmrvizzy.skyblocker.mixin;
-
-import me.xmrvizzy.skyblocker.chat.ChatFilterResult;
-import me.xmrvizzy.skyblocker.chat.ChatMessageListener;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.hud.ChatHud;
-import net.minecraft.client.gui.hud.MessageIndicator;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.network.message.MessageSignatureData;
-import net.minecraft.text.Text;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-@Mixin(ChatHud.class)
-public abstract class ChatHudListenerMixin {
- @Shadow
- @Final
- private MinecraftClient client;
-
- @Inject(method = "addMessage(Lnet/minecraft/text/Text;Lnet/minecraft/network/message/MessageSignatureData;ILnet/minecraft/client/gui/hud/MessageIndicator;Z)V", at = @At("HEAD"), cancellable = true)
- public void skyblocker$onMessage(Text message, MessageSignatureData signature, int ticks, MessageIndicator indicator, boolean refresh, CallbackInfo ci) {
- if (!Utils.isOnSkyblock)
- return;
- String asString = message.getString();
- ChatFilterResult result = ChatMessageListener.EVENT.invoker().onMessage(message, asString);
- switch (result) {
- case ACTION_BAR:
- ClientPlayerEntity player = client.player;
- if (player != null)
- player.sendMessage(message, true);
- case FILTER:
- ci.cancel();
- }
- }
-
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
new file mode 100644
index 00000000..80c98473
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import me.xmrvizzy.skyblocker.skyblock.FishingHelper;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(ClientPlayNetworkHandler.class)
+public abstract class ClientPlayNetworkHandlerMixin {
+ @Shadow
+ @Final
+ private MinecraftClient client;
+
+ @Inject(method = "onPlaySound", at = @At("RETURN"))
+ private void skyblockmod_onPlaySound(PlaySoundS2CPacket packet, CallbackInfo ci) {
+ FishingHelper.onSound(client, packet);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java
index 237f6ba9..e48e725e 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java
@@ -2,27 +2,31 @@ package me.xmrvizzy.skyblocker.mixin;
import com.mojang.authlib.GameProfile;
import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock;
+import me.xmrvizzy.skyblocker.skyblock.rift.HealingMelonIndicator;
import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
-// import net.minecraft.network.encryption.PlayerPublicKey;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ClientPlayerEntity.class)
public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity {
-
- // public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile, PlayerPublicKey publicKey) {
public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) {
- // super(world, profile, publicKey);
super(world, profile);
}
@Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true)
public void skyblocker$dropSelectedItem(boolean dropEntireStack, CallbackInfoReturnable<Boolean> cir) {
- if (Utils.isOnSkyblock) HotbarSlotLock.handleDropSelectedItem(this.getInventory().selectedSlot, cir);
+ if (Utils.isOnSkyblock()) HotbarSlotLock.handleDropSelectedItem(this.getInventory().selectedSlot, cir);
+ }
+
+ @Inject(method = "updateHealth", at = @At("HEAD"))
+ public void skyblocker$updateHealth(float health, CallbackInfo info) {
+ HealingMelonIndicator.updateHealth(MinecraftClient.getInstance());
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemRendererMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
index b9ddc156..a8a490b8 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemRendererMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java
@@ -5,6 +5,8 @@ import java.util.regex.Pattern;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -15,19 +17,25 @@ import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.ItemUtils;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.gui.DrawableHelper;
-import net.minecraft.client.render.item.ItemRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.ColorHelper;
-@Mixin(ItemRenderer.class)
-public abstract class ItemRendererMixin {
- @Inject(method = "renderGuiItemOverlay(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD"))
- public void skyblocker$renderItemBar(MatrixStack matrices, TextRenderer renderer, ItemStack stack, int x, int y, @Nullable String countLabel, CallbackInfo ci) {
+@Mixin(DrawContext.class)
+public abstract class DrawContextMixin {
+ @Shadow @Final private MatrixStack matrices;
+
+ @Shadow
+ public void fill(RenderLayer layer, int x1, int x2, int y1, int y2, int color) {
+ }
+
+ @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD"))
+ public void skyblocker$renderItemBar(TextRenderer textRenderer, ItemStack stack, int x, int y, @Nullable String countOverride, CallbackInfo ci) {
- if (Utils.isOnSkyblock && SkyblockerConfig.get().locations.dwarvenMines.enableDrillFuel) {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().locations.dwarvenMines.enableDrillFuel) {
if (!stack.isEmpty()) {
NbtCompound tag = stack.getNbt();
if (tag != null && tag.contains("ExtraAttributes")) {
@@ -44,13 +52,18 @@ public abstract class ItemRendererMixin {
break;
}
}
-
+
+ matrices.push();
+ matrices.translate(0f, 0f, 200f);
RenderSystem.disableDepthTest();
+
float hue = Math.max(0.0F, 1.0F - (max - current) / max);
int width = Math.round(current / max * 13.0F);
Color color = Color.getHSBColor(hue / 3.0F, 1.0F, 1.0F);
- DrawableHelper.fill(matrices, x + 2, y + 13, x + 15, y + 15, 0xFF000000);
- DrawableHelper.fill(matrices, x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()));
+ this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 15, y + 15, 0xFF000000);
+ this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()));
+
+ matrices.pop();
RenderSystem.enableDepthTest();
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java
index 29da4bd6..761f8a68 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java
@@ -22,10 +22,11 @@ public abstract class FarmlandBlockMixin extends Block {
@Inject(method = "getOutlineShape", at = @At("HEAD"), cancellable = true)
public void skyblocker$onGetOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context, CallbackInfoReturnable<VoxelShape> cir) {
- if (Utils.isOnSkyblock && SkyblockerConfig.get().general.hitbox.oldFarmlandHitbox)
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.hitbox.oldFarmlandHitbox)
cir.setReturnValue(Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 16.0));
}
+ @SuppressWarnings("deprecation")
@Override
public VoxelShape getCullingShape(BlockState state, BlockView world, BlockPos pos) {
return Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 15.0, 16.0);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenMixin.java
deleted file mode 100644
index d63d17b8..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenMixin.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package me.xmrvizzy.skyblocker.mixin;
-
-import me.xmrvizzy.skyblocker.SkyblockerMod;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
-import net.minecraft.client.gui.screen.ingame.HandledScreen;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.player.PlayerInventory;
-import net.minecraft.screen.GenericContainerScreenHandler;
-import net.minecraft.text.Text;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-
-@Mixin(GenericContainerScreen.class)
-public abstract class GenericContainerScreenMixin extends HandledScreen<GenericContainerScreenHandler> {
- @Shadow
- @Final
- private int rows;
-
- public GenericContainerScreenMixin(GenericContainerScreenHandler handler, PlayerInventory inventory, Text title) {
- super(handler, inventory, title);
- }
-
- @Override
- protected void drawForeground(MatrixStack matrices, int mouseX, int mouseY) {
- super.drawForeground(matrices, mouseX, mouseY);
- if (Utils.isOnSkyblock)
- SkyblockerMod.getInstance().containerSolverManager.onDraw(matrices, this.handler.slots.subList(0, rows * 9));
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
index 44b1b5c8..903d3992 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java
@@ -1,26 +1,35 @@
package me.xmrvizzy.skyblocker.mixin;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.gui.ContainerSolver;
import me.xmrvizzy.skyblocker.skyblock.BackpackPreview;
+import me.xmrvizzy.skyblocker.skyblock.experiment.ChronomatronSolver;
+import me.xmrvizzy.skyblocker.skyblock.experiment.ExperimentSolver;
+import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver;
+import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver;
import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup;
-import me.xmrvizzy.skyblocker.skyblock.quicknav.QuickNav;
-import me.xmrvizzy.skyblocker.skyblock.quicknav.QuickNavButton;
import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
-import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.inventory.SimpleInventory;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
+import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.text.Text;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
+import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
-import java.util.List;
-
@Mixin(HandledScreen.class)
public abstract class HandledScreenMixin extends Screen {
protected HandledScreenMixin(Text title) {
@@ -31,34 +40,65 @@ public abstract class HandledScreenMixin extends Screen {
@Nullable
protected Slot focusedSlot;
- @Inject(method = "init()V", at = @At("TAIL"))
- private void skyblocker$init(CallbackInfo ci) {
- // quicknav
- if (Utils.isOnSkyblock && SkyblockerConfig.get().quickNav.enableQuickNav) {
- String screenTitle = super.getTitle().getString().trim();
- List<QuickNavButton> buttons = QuickNav.init(screenTitle);
- for (QuickNavButton button : buttons) super.addDrawableChild(button);
- }
- // backpack preview
- BackpackPreview.updateStorage((HandledScreen<?>) (Object) this);
- }
-
@Inject(at = @At("HEAD"), method = "keyPressed")
public void skyblocker$keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) {
- if (this.focusedSlot != null) {
- if (keyCode != 256 && !this.client.options.inventoryKey.matchesKey(keyCode, scanCode)) {
- if (WikiLookup.wikiLookup.matchesKey(keyCode, scanCode)) WikiLookup.openWiki(this.focusedSlot);
- }
+ if (this.client != null && this.focusedSlot != null && keyCode != 256 && !this.client.options.inventoryKey.matchesKey(keyCode, scanCode) && WikiLookup.wikiLookup.matchesKey(keyCode, scanCode)) {
+ WikiLookup.openWiki(this.focusedSlot);
}
}
@Inject(at = @At("HEAD"), method = "drawMouseoverTooltip", cancellable = true)
- public void skyblocker$drawMouseOverTooltip(MatrixStack matrices, int x, int y, CallbackInfo ci) {
- String title = this.getTitle().getString();
+ public void skyblocker$drawMouseOverTooltip(DrawContext context, int x, int y, CallbackInfo ci) {
+ // Hide Empty Tooltips
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.hideEmptyTooltips && this.focusedSlot != null && focusedSlot.getStack().getName().getString().equals(" ")) {
+ ci.cancel();
+ }
+
+ // Backpack Preview
boolean shiftDown = SkyblockerConfig.get().general.backpackPreviewWithoutShift ^ Screen.hasShiftDown();
- if (shiftDown && title.equals("Storage") && this.focusedSlot != null) {
- if (this.focusedSlot.inventory == this.client.player.getInventory()) return;
- if (BackpackPreview.renderPreview(matrices, this.focusedSlot.getIndex(), x, y)) ci.cancel();
+ if (this.client != null && this.client.player != null && this.focusedSlot != null && shiftDown && this.getTitle().getString().equals("Storage") && this.focusedSlot.inventory != this.client.player.getInventory() && BackpackPreview.renderPreview(context, this.focusedSlot.getIndex(), x, y)) {
+ ci.cancel();
+ }
+ }
+
+ @Redirect(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/slot/Slot;getStack()Lnet/minecraft/item/ItemStack;", ordinal = 0))
+ private ItemStack skyblocker$experimentSolvers$replaceTooltipDisplayStack(Slot slot) {
+ return skyblocker$experimentSolvers$getStack(slot, null);
+ }
+
+ @ModifyVariable(method = "drawSlot", at = @At(value = "LOAD", ordinal = 4), ordinal = 0)
+ private ItemStack skyblocker$experimentSolvers$replaceDisplayStack(ItemStack stack, DrawContext context, Slot slot) {
+ return skyblocker$experimentSolvers$getStack(slot, stack);
+ }
+
+ @Unique
+ private ItemStack skyblocker$experimentSolvers$getStack(Slot slot, ItemStack stack) {
+ ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver();
+ if ((currentSolver instanceof SuperpairsSolver || currentSolver instanceof UltrasequencerSolver) && ((ExperimentSolver) currentSolver).getState() == ExperimentSolver.State.SHOW && slot.inventory instanceof SimpleInventory) {
+ ItemStack itemStack = ((ExperimentSolver) currentSolver).getSlots().get(slot.getIndex());
+ return itemStack == null ? slot.getStack() : itemStack;
+ }
+ return (stack != null) ? stack : slot.getStack();
+ }
+
+ @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;clickSlot(IIILnet/minecraft/screen/slot/SlotActionType;Lnet/minecraft/entity/player/PlayerEntity;)V"))
+ private void skyblocker$experimentSolvers$onSlotClick(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) {
+ if (slot != null) {
+ ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver();
+ if (currentSolver instanceof ExperimentSolver experimentSolver && experimentSolver.getState() == ExperimentSolver.State.SHOW && slot.inventory instanceof SimpleInventory) {
+ if (experimentSolver instanceof ChronomatronSolver chronomatronSolver) {
+ Item item = chronomatronSolver.getChronomatronSlots().get(chronomatronSolver.getChronomatronCurrentOrdinal());
+ if ((slot.getStack().isOf(item) || ChronomatronSolver.TERRACOTTA_TO_GLASS.get(slot.getStack().getItem()) == item) && chronomatronSolver.incrementChronomatronCurrentOrdinal() >= chronomatronSolver.getChronomatronSlots().size()) {
+ chronomatronSolver.setState(ExperimentSolver.State.END);
+ }
+ } else if (experimentSolver instanceof SuperpairsSolver superpairsSolver) {
+ superpairsSolver.setSuperpairsPrevClickedSlot(slot.getIndex());
+ superpairsSolver.setSuperpairsCurrentSlot(ItemStack.EMPTY);
+ } else if (experimentSolver instanceof UltrasequencerSolver ultrasequencerSolver && slot.getIndex() == ultrasequencerSolver.getUltrasequencerNextSlot()) {
+ int count = ultrasequencerSolver.getSlots().get(ultrasequencerSolver.getUltrasequencerNextSlot()).getCount() + 1;
+ ultrasequencerSolver.getSlots().entrySet().stream().filter(entry -> entry.getValue().getCount() == count).findAny().ifPresentOrElse((entry) -> ultrasequencerSolver.setUltrasequencerNextSlot(entry.getKey()), () -> ultrasequencerSolver.setState(ExperimentSolver.State.END));
+ }
+ }
}
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
index c137e6c8..7d9182b5 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java
@@ -1,6 +1,5 @@
package me.xmrvizzy.skyblocker.mixin;
-import com.mojang.blaze3d.systems.RenderSystem;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars;
@@ -10,13 +9,13 @@ import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
-import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.hud.InGameHud;
-import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
+
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -26,7 +25,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Environment(EnvType.CLIENT)
@Mixin(InGameHud.class)
-public abstract class InGameHudMixin extends DrawableHelper {
+public abstract class InGameHudMixin {
+ //@Shadow
+ //@Final
+ //private static Identifier ICONS = new Identifier("textures/gui/icons.png");
@Unique
private static final Identifier SLOT_LOCK = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/slot_lock.png");
@Unique
@@ -35,7 +37,7 @@ public abstract class InGameHudMixin extends DrawableHelper {
@Unique
private final FancyStatusBars statusBars = new FancyStatusBars();
@Unique
- private MatrixStack hotbarMatrices;
+ private DrawContext hotbarContext;
@Unique
private int hotbarSlotIndex;
@@ -50,7 +52,7 @@ public abstract class InGameHudMixin extends DrawableHelper {
@Inject(method = "setOverlayMessage(Lnet/minecraft/text/Text;Z)V", at = @At("HEAD"), cancellable = true)
private void skyblocker$onSetOverlayMessage(Text message, boolean tinted, CallbackInfo ci) {
- if (!Utils.isOnSkyblock || !SkyblockerConfig.get().general.bars.enableBars)
+ if (!Utils.isOnSkyblock() || !SkyblockerConfig.get().general.bars.enableBars || Utils.isInTheRift())
return;
String msg = message.getString();
String res = statusBarTracker.update(msg, SkyblockerConfig.get().messages.hideMana);
@@ -62,46 +64,45 @@ public abstract class InGameHudMixin extends DrawableHelper {
}
@Inject(method = "renderHotbar", at = @At("HEAD"))
- public void skyblocker$renderHotbar(float f, MatrixStack matrices, CallbackInfo ci) {
- if (Utils.isOnSkyblock) {
- hotbarMatrices = matrices;
+ public void skyblocker$renderHotbar(float f, DrawContext context, CallbackInfo ci) {
+ if (Utils.isOnSkyblock()) {
+ hotbarContext = context;
hotbarSlotIndex = 0;
}
}
@Inject(method = "renderHotbarItem", at = @At("HEAD"))
- public void skyblocker$renderHotbarItem(MatrixStack matrices, int i, int j, float f, PlayerEntity player, ItemStack stack, int seed, CallbackInfo ci) {
- if (Utils.isOnSkyblock) {
+ public void skyblocker$renderHotbarItem(DrawContext context, int i, int j, float f, PlayerEntity player, ItemStack stack, int seed, CallbackInfo ci) {
+ if (Utils.isOnSkyblock()) {
if (HotbarSlotLock.isLocked(hotbarSlotIndex)) {
- RenderSystem.setShaderTexture(0, SLOT_LOCK);
- DrawableHelper.drawTexture(hotbarMatrices, i, j, 0, 0, 16, 16);
+ hotbarContext.drawTexture(SLOT_LOCK, i, j, 0, 0, 16, 16);
}
hotbarSlotIndex++;
}
}
@Inject(method = "renderExperienceBar", at = @At("HEAD"), cancellable = true)
- private void skyblocker$renderExperienceBar(MatrixStack matrices, int x, CallbackInfo ci) {
- if (Utils.isOnSkyblock && SkyblockerConfig.get().general.bars.enableBars)
+ private void skyblocker$renderExperienceBar(DrawContext context, int x, CallbackInfo ci) {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.bars.enableBars && !Utils.isInTheRift())
ci.cancel();
}
@Inject(method = "renderStatusBars", at = @At("HEAD"), cancellable = true)
- private void skyblocker$renderStatusBars(MatrixStack matrices, CallbackInfo ci) {
- if (!Utils.isOnSkyblock)
+ private void skyblocker$renderStatusBars(DrawContext context, CallbackInfo ci) {
+ if (!Utils.isOnSkyblock())
return;
- if (statusBars.render(matrices, scaledWidth, scaledHeight))
+ if (statusBars.render(context, scaledWidth, scaledHeight))
ci.cancel();
- if (Utils.isInDungeons && SkyblockerConfig.get().locations.dungeons.enableMap)
- DungeonMap.render(matrices);
+ if (Utils.isInDungeons() && SkyblockerConfig.get().locations.dungeons.enableMap)
+ DungeonMap.render(context.getMatrices());
- RenderSystem.setShaderTexture(0, GUI_ICONS_TEXTURE);
+ //RenderSystem.setShaderTexture(0, ICONS);
}
@Inject(method = "renderMountHealth", at = @At("HEAD"), cancellable = true)
- private void skyblocker$renderMountHealth(MatrixStack matrices, CallbackInfo ci) {
- if (Utils.isOnSkyblock && SkyblockerConfig.get().general.bars.enableBars)
+ private void skyblocker$renderMountHealth(DrawContext context, CallbackInfo ci) {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.bars.enableBars && !Utils.isInTheRift())
ci.cancel();
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java
index 8de390cc..64a1a4fe 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java
@@ -19,7 +19,7 @@ public abstract class InventoryScreenMixin {
)
)
RecipeBookWidget skyblocker$constructor() {
- if (Utils.isOnSkyblock && SkyblockerConfig.get().general.itemList.enableItemList)
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.itemList.enableItemList)
return new ItemListWidget();
else
return new RecipeBookWidget();
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java
index c28a7096..505de202 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java
@@ -22,7 +22,7 @@ public abstract class LeverBlockMixin extends WallMountedBlock {
@Inject(method = "getOutlineShape", at = @At("HEAD"), cancellable = true)
public void skyblocker$onGetOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context, CallbackInfoReturnable<VoxelShape> cir) {
- if (Utils.isOnSkyblock) {
+ if (Utils.isOnSkyblock()) {
VoxelShape shape = OldLever.getShape(state.get(FACE), state.get(FACING));
if (shape != null)
cir.setReturnValue(shape);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java
index 2bc47bba..0c1977fe 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java
@@ -1,12 +1,8 @@
package me.xmrvizzy.skyblocker.mixin;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
-import me.xmrvizzy.skyblocker.container.ContainerSolverManager;
import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.client.network.ClientPlayerEntity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
@@ -17,27 +13,14 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin {
-
@Shadow
@Nullable
public ClientPlayerEntity player;
- @Inject(method = "tick", at = @At("HEAD"))
- public void skyblocker$tick(CallbackInfo ci) {
- SkyblockerMod.getInstance().onTick();
- }
-
@Inject(method = "handleInputEvents", at = @At("HEAD"))
public void skyblocker$handleInputEvents(CallbackInfo ci) {
- if (Utils.isOnSkyblock) HotbarSlotLock.handleInputEvents(player);
- }
-
- @Inject(method = "setScreen", at = @At("HEAD"))
- public void skyblocker$onSetScreen(Screen screen, CallbackInfo ci) {
- ContainerSolverManager manager = SkyblockerMod.getInstance().containerSolverManager;
- if (Utils.isOnSkyblock && screen instanceof GenericContainerScreen)
- manager.onSetScreen((GenericContainerScreen) screen);
- else
- manager.clearScreen();
+ if (Utils.isOnSkyblock()) {
+ HotbarSlotLock.handleInputEvents(player);
+ }
}
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java
new file mode 100644
index 00000000..db329775
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudAccessor.java
@@ -0,0 +1,18 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import java.util.Comparator;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Accessor;
+
+import net.minecraft.client.gui.hud.PlayerListHud;
+import net.minecraft.client.network.PlayerListEntry;
+
+@Mixin(PlayerListHud.class)
+public interface PlayerListHudAccessor {
+
+ @Accessor("ENTRY_ORDERING")
+ public static Comparator<PlayerListEntry> getOrdering() {
+ throw new AssertionError();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
new file mode 100644
index 00000000..ef65190f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java
@@ -0,0 +1,57 @@
+package me.xmrvizzy.skyblocker.mixin;
+
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.hud.PlayerListHud;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraft.scoreboard.ScoreboardObjective;
+import net.minecraft.text.Text;
+
+@Environment(EnvType.CLIENT)
+@Mixin(PlayerListHud.class)
+public class PlayerListHudMixin {
+
+ @Shadow
+ private Text footer;
+
+ @Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/client/gui/DrawContext;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreboardObjective;)V", cancellable = true)
+ public void skyblocker$renderTabHud(DrawContext context, int scaledW, Scoreboard sb, ScoreboardObjective sbo,
+ CallbackInfo info) {
+
+ if (!Utils.isOnSkyblock()
+ || !SkyblockerConfig.get().general.tabHud.tabHudEnabled
+ || TabHud.defaultTgl.isPressed()) {
+ return;
+ }
+
+ MinecraftClient client = MinecraftClient.getInstance();
+ ClientPlayNetworkHandler nwH = client.getNetworkHandler();
+ if (nwH == null) {
+ return;
+ }
+
+ int w = scaledW;
+ int h = MinecraftClient.getInstance().getWindow().getScaledHeight();
+ try {
+ Screen screen = Screen.getCorrect(w, h, footer);
+ screen.render(context);
+ info.cancel();
+ } catch (Exception e) {
+ TabHud.LOGGER.error("Drawing default hud. Reason: Screen exception {}", e);
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ScreenMixin.java
deleted file mode 100644
index 14aa8868..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ScreenMixin.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package me.xmrvizzy.skyblocker.mixin;
-
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.utils.Utils;
-
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.item.ItemStack;
-import net.minecraft.text.Text;
-
-@Mixin(Screen.class)
-public abstract class ScreenMixin {
-
- @Inject(at = @At("HEAD"), method = "renderTooltip(Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/item/ItemStack;II)V", cancellable = true)
- public void skyblocker$renderTooltip(MatrixStack matrices, ItemStack itemStack, int x, int y, CallbackInfo ci) {
- Text stackName = itemStack.getName();
- String strName = stackName.getString();
- if (Utils.isOnSkyblock && SkyblockerConfig.get().general.hideEmptyTooltips && strName.equals(" ")) {
- ci.cancel();
- }
- }
-
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java
new file mode 100644
index 00000000..ff7c7cbc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java
@@ -0,0 +1,16 @@
+package me.xmrvizzy.skyblocker.mixin.accessor;
+
+import net.minecraft.client.render.VertexConsumerProvider;
+import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer;
+import net.minecraft.client.util.math.MatrixStack;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+@Mixin(BeaconBlockEntityRenderer.class)
+public interface BeaconBlockEntityRendererInvoker {
+ @SuppressWarnings("unused")
+ @Invoker("renderBeam")
+ static void renderBeam(MatrixStack matrices, VertexConsumerProvider vertexConsumers, float tickDelta, long worldTime, int yOffset, int maxY, float[] color) {
+ throw new IllegalStateException("Mixin invoker failed to apply.");
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java
new file mode 100644
index 00000000..108a7344
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java
@@ -0,0 +1,15 @@
+package me.xmrvizzy.skyblocker.mixin.accessor;
+
+import me.xmrvizzy.skyblocker.utils.FrustumUtils;
+import net.minecraft.client.render.Frustum;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+/**
+ * Use {@link FrustumUtils#isVisible(double, double, double, double, double, double) FrustumUtils#isVisible} which is shorter. For the purpose of avoiding object allocations!
+ */
+@Mixin(Frustum.class)
+public interface FrustumInvoker {
+ @Invoker("isVisible")
+ boolean isVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java
index d8cca051..d89a18e0 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/BackpackPreview.java
@@ -3,10 +3,11 @@ package me.xmrvizzy.skyblocker.skyblock;
import com.mojang.blaze3d.systems.RenderSystem;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.network.PlayerListEntry;
@@ -26,7 +27,7 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class BackpackPreview extends DrawableHelper {
+public class BackpackPreview {
private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png");
private static final BackpackPreview instance = new BackpackPreview();
private static final Pattern PROFILE_PATTERN = Pattern.compile("Profile: ([a-zA-Z]+)");
@@ -40,9 +41,17 @@ public class BackpackPreview extends DrawableHelper {
private static String loaded = ""; // uuid + sb profile currently loaded
private static Path save_dir = null;
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (screen instanceof HandledScreen<?> handledScreen) {
+ updateStorage(handledScreen);
+ }
+ });
+ }
+
public static void tick() {
- Utils.sbChecker(); // force update isOnSkyblock to prevent crash on disconnect
- if (Utils.isOnSkyblock) {
+ Utils.update(); // force update isOnSkyblock to prevent crash on disconnect
+ if (Utils.isOnSkyblock()) {
// save all dirty storages
saveStorage();
// update save dir based on uuid and sb profile
@@ -126,7 +135,7 @@ public class BackpackPreview extends DrawableHelper {
}
}
- public static boolean renderPreview(MatrixStack matrices, int index, int mouseX, int mouseY) {
+ public static boolean renderPreview(DrawContext context, int index, int mouseX, int mouseY) {
if (index >= 9 && index < 18) index -= 9;
else if (index >= 27 && index < 45) index -= 18;
else return false;
@@ -140,12 +149,13 @@ public class BackpackPreview extends DrawableHelper {
RenderSystem.disableDepthTest();
RenderSystem.setShaderTexture(0, TEXTURE);
- BackpackPreview.drawTexture(matrices, x, y, 0, 0, 176, 7);
+ context.drawTexture(TEXTURE, x, y, 0, 0, 176, 7);
for (int i = 0; i < rows; ++i)
- BackpackPreview.drawTexture(matrices, x, y + i * 18 + 7, 0, 7, 176, 18);
- BackpackPreview.drawTexture(matrices, x, y + rows * 18 + 7, 0, 25, 176, 7);
+ context.drawTexture(TEXTURE, x, y + i * 18 + 7, 0, 7, 176, 18);
+ context.drawTexture(TEXTURE, x, y + rows * 18 + 7, 0, 25, 176, 7);
RenderSystem.enableDepthTest();
+ MatrixStack matrices = context.getMatrices();
ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
for (int i = 9; i < storage[index].size(); ++i) {
@@ -153,8 +163,8 @@ public class BackpackPreview extends DrawableHelper {
int itemY = y + (i - 9) / 9 * 18 + 8;
matrices.push();
matrices.translate(0, 0, 200);
- itemRenderer.renderInGui(matrices, storage[index].getStack(i), itemX, itemY);
- itemRenderer.renderGuiItemOverlay(matrices, textRenderer, storage[index].getStack(i), itemX, itemY);
+ context.drawItem(storage[index].getStack(i), itemX, itemY);
+ context.drawItemInSlot(textRenderer, storage[index].getStack(i), itemX, itemY);
matrices.pop();
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java
new file mode 100644
index 00000000..ccffdcca
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java
@@ -0,0 +1,175 @@
+package me.xmrvizzy.skyblocker.skyblock;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.NEURepo;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.text.Text;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class FairySouls {
+ private static final Logger LOGGER = LoggerFactory.getLogger(FairySouls.class);
+ private static CompletableFuture<Void> fairySoulsLoaded;
+ private static final Map<String, Set<BlockPos>> fairySouls = new HashMap<>();
+ private static final Map<String, Map<String, Set<BlockPos>>> foundFairies = new HashMap<>();
+
+ public static void init() {
+ fairySoulsLoaded = NEURepo.runAsyncAfterLoad(() -> {
+ try {
+ BufferedReader reader = new BufferedReader(new FileReader(NEURepo.LOCAL_REPO_DIR.resolve("constants").resolve("fairy_souls.json").toFile()));
+ for (Map.Entry<String, JsonElement> fairySoulJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) {
+ if (fairySoulJson.getKey().equals("//") || fairySoulJson.getKey().equals("Max Souls")) {
+ continue;
+ }
+ ImmutableSet.Builder<BlockPos> fairySoulsForLocation = ImmutableSet.builder();
+ for (JsonElement fairySoul : fairySoulJson.getValue().getAsJsonArray().asList()) {
+ fairySoulsForLocation.add(parseBlockPos(fairySoul));
+ }
+ fairySouls.put(fairySoulJson.getKey(), fairySoulsForLocation.build());
+ }
+ reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()));
+ for (Map.Entry<String, JsonElement> foundFairiesForProfileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) {
+ Map<String, Set<BlockPos>> foundFairiesForProfile = new HashMap<>();
+ for (Map.Entry<String, JsonElement> foundFairiesForLocationJson : foundFairiesForProfileJson.getValue().getAsJsonObject().asMap().entrySet()) {
+ Set<BlockPos> foundFairiesForLocation = new HashSet<>();
+ for (JsonElement foundFairy : foundFairiesForLocationJson.getValue().getAsJsonArray().asList()) {
+ foundFairiesForLocation.add(parseBlockPos(foundFairy));
+ }
+ foundFairiesForProfile.put(foundFairiesForLocationJson.getKey(), foundFairiesForLocation);
+ }
+ foundFairies.put(foundFairiesForProfileJson.getKey(), foundFairiesForProfile);
+ }
+ reader.close();
+ } catch (IOException e) {
+ LOGGER.error("Failed to load found fairy souls", e);
+ } catch (Exception e) {
+ LOGGER.error("Encountered unknown exception loading fairy souls", e);
+ }
+ });
+ ClientLifecycleEvents.CLIENT_STOPPING.register(FairySouls::saveFoundFairySouls);
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(FairySouls::render);
+ ClientReceiveMessageEvents.GAME.register(FairySouls::onChatMessage);
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE)
+ .then(literal("fairySouls")
+ .then(literal("markAllInCurrentIslandFound").executes(context -> {
+ FairySouls.markAllFairiesFound();
+ context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllFound"));
+ return 1;
+ }))
+ .then(literal("markAllInCurrentIslandMissing").executes(context -> {
+ FairySouls.markAllFairiesNotFound();
+ context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllMissing"));
+ return 1;
+ })))));
+ }
+
+ private static BlockPos parseBlockPos(JsonElement posJson) {
+ String[] posArray = posJson.getAsString().split(",");
+ return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2]));
+ }
+
+ public static void saveFoundFairySouls(MinecraftClient client) {
+ try {
+ BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()));
+ JsonObject foundFairiesJson = new JsonObject();
+ for (Map.Entry<String, Map<String, Set<BlockPos>>> foundFairiesForProfile : foundFairies.entrySet()) {
+ JsonObject foundFairiesForProfileJson = new JsonObject();
+ for (Map.Entry<String, Set<BlockPos>> foundFairiesForLocation : foundFairiesForProfile.getValue().entrySet()) {
+ JsonArray foundFairiesForLocationJson = new JsonArray();
+ for (BlockPos foundFairy : foundFairiesForLocation.getValue()) {
+ foundFairiesForLocationJson.add(foundFairy.getX() + "," + foundFairy.getY() + "," + foundFairy.getZ());
+ }
+ foundFairiesForProfileJson.add(foundFairiesForLocation.getKey(), foundFairiesForLocationJson);
+ }
+ foundFairiesJson.add(foundFairiesForProfile.getKey(), foundFairiesForProfileJson);
+ }
+ SkyblockerMod.GSON.toJson(foundFairiesJson, writer);
+ writer.close();
+ } catch (IOException e) {
+ LOGGER.error("Failed to write found fairy souls to file.");
+ }
+ }
+
+ public static void render(WorldRenderContext context) {
+ if (SkyblockerConfig.get().general.fairySouls.enableFairySoulsHelper && fairySoulsLoaded.isDone() && fairySouls.containsKey(Utils.getLocationRaw())) {
+ for (BlockPos fairySoul : fairySouls.get(Utils.getLocationRaw())) {
+ float[] colorComponents = isFairySoulNotFound(fairySoul) ? DyeColor.GREEN.getColorComponents() : DyeColor.RED.getColorComponents();
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoul, colorComponents, 0.5F);
+ }
+ }
+ }
+
+ private static boolean isFairySoulNotFound(BlockPos fairySoul) {
+ Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile());
+ if (foundFairiesForProfile == null) {
+ return true;
+ }
+ Set<BlockPos> foundFairiesForProfileAndLocation = foundFairiesForProfile.get(Utils.getLocationRaw());
+ if (foundFairiesForProfileAndLocation == null) {
+ return true;
+ }
+ return !foundFairiesForProfileAndLocation.contains(fairySoul);
+ }
+
+ public static void onChatMessage(Text text, boolean overlay) {
+ String message = text.getString();
+ if (message.equals("You have already found that Fairy Soul!") || message.equals("SOUL! You found a Fairy Soul!")) {
+ markClosestFairyFound();
+ }
+ }
+
+ private static void markClosestFairyFound() {
+ PlayerEntity player = MinecraftClient.getInstance().player;
+ if (player == null) {
+ LOGGER.warn("Failed to mark closest fairy soul as found because player is null.");
+ return;
+ }
+ fairySouls.get(Utils.getLocationRaw()).stream().filter(FairySouls::isFairySoulNotFound).min(Comparator.comparingDouble(fairySoul -> fairySoul.getSquaredDistance(player.getPos()))).ifPresent(fairySoul -> {
+ initializeFoundFairiesForCurrentProfileAndLocation();
+ foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).add(fairySoul);
+ });
+ }
+
+ public static void markAllFairiesFound() {
+ initializeFoundFairiesForCurrentProfileAndLocation();
+ foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).addAll(fairySouls.get(Utils.getLocationRaw()));
+ }
+
+ public static void markAllFairiesNotFound() {
+ Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile());
+ if (foundFairiesForProfile != null) {
+ foundFairiesForProfile.remove(Utils.getLocationRaw());
+ }
+ }
+
+ private static void initializeFoundFairiesForCurrentProfileAndLocation() {
+ initializeFoundFairiesForProfileAndLocation(Utils.getProfile(), Utils.getLocationRaw());
+ }
+
+ private static void initializeFoundFairiesForProfileAndLocation(String profile, String location) {
+ foundFairies.computeIfAbsent(profile, profileKey -> new HashMap<>());
+ foundFairies.get(profile).computeIfAbsent(location, locationKey -> new HashSet<>());
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java
index 05b57410..1a460f5b 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java
@@ -1,15 +1,14 @@
package me.xmrvizzy.skyblocker.skyblock;
-import com.mojang.blaze3d.systems.RenderSystem;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.gui.DrawableHelper;
-import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.util.Identifier;
-public class FancyStatusBars extends DrawableHelper {
+public class FancyStatusBars {
private static final Identifier BARS = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/bars.png");
private final MinecraftClient client = MinecraftClient.getInstance();
@@ -39,9 +38,9 @@ public class FancyStatusBars extends DrawableHelper {
return (100 * value) / max;
}
- public boolean render(MatrixStack matrices, int scaledWidth, int scaledHeight) {
+ public boolean render(DrawContext context, int scaledWidth, int scaledHeight) {
var player = client.player;
- if (!SkyblockerConfig.get().general.bars.enableBars || player == null)
+ if (!SkyblockerConfig.get().general.bars.enableBars || player == null || Utils.isInTheRift())
return false;
anchorsX[0] = scaledWidth / 2 - 91;
anchorsY[0] = scaledHeight - 33;
@@ -72,11 +71,12 @@ public class FancyStatusBars extends DrawableHelper {
moveBar(i, configAnchorNum);
}
- RenderSystem.setShaderTexture(0, BARS);
- for (var bar : bars)
- bar.draw(matrices);
- for (var bar : bars)
- bar.drawText(matrices);
+ for (var bar : bars) {
+ bar.draw(context);
+ }
+ for (var bar : bars) {
+ bar.drawText(context);
+ }
return true;
}
@@ -143,32 +143,36 @@ public class FancyStatusBars extends DrawableHelper {
this.text = val;
}
- public void draw(MatrixStack matrices) {
+ public void draw(DrawContext context) {
// Dont draw if anchorNum is outside of range
if (anchorNum < 0 || anchorNum > 2) return;
// Draw the icon for the bar
- drawTexture(matrices, anchorsX[anchorNum] + offsetX, anchorsY[anchorNum], 0, v, 9, 9);
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX, anchorsY[anchorNum], 0, v, 9, 9);
// Draw the background for the bar
- drawTexture(matrices, anchorsX[anchorNum] + offsetX + 10, anchorsY[anchorNum], 10, v, 2, 9);
- for (int i = 2; i < bar_width - 2; i += 58)
- drawTexture(matrices, anchorsX[anchorNum] + offsetX + 10 + i, anchorsY[anchorNum], 12, v, Math.min(58, bar_width - 2 - i), 9);
- drawTexture(matrices, anchorsX[anchorNum] + offsetX + 10 + bar_width - 2, anchorsY[anchorNum], 70, v, 2, 9);
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10, anchorsY[anchorNum], 10, v, 2, 9);
+ for (int i = 2; i < bar_width - 2; i += 58) {
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + i, anchorsY[anchorNum], 12, v, Math.min(58, bar_width - 2 - i), 9);
+ }
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + bar_width - 2, anchorsY[anchorNum], 70, v, 2, 9);
// Draw the filled part of the bar
for (int i = 0; i < fill.length; i++) {
int fill_width = this.fill[i] * (bar_width - 2) / 100;
- if (fill_width >= 1)
- drawTexture(matrices, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum], 72 + i*60, v, 1, 9);
- for (int j = 1; j < fill_width - 1; j += 58)
- drawTexture(matrices, anchorsX[anchorNum] + offsetX + 11 + j, anchorsY[anchorNum], 73 + i*60, v, Math.min(58, fill_width - 1 - j), 9);
- if (fill_width == bar_width - 2)
- drawTexture(matrices, anchorsX[anchorNum] + offsetX + 11 + fill_width - 1, anchorsY[anchorNum], 131 + i*60, v, 1, 9);
+ if (fill_width >= 1) {
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum], 72 + i * 60, v, 1, 9);
+ }
+ for (int j = 1; j < fill_width - 1; j += 58) {
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + j, anchorsY[anchorNum], 73 + i * 60, v, Math.min(58, fill_width - 1 - j), 9);
+ }
+ if (fill_width == bar_width - 2) {
+ context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + fill_width - 1, anchorsY[anchorNum], 131 + i * 60, v, 1, 9);
+ }
}
}
- public void drawText(MatrixStack matrices) {
+ public void drawText(DrawContext context) {
// Dont draw if anchorNum is outside of range
if (anchorNum < 0 || anchorNum > 2) return;
@@ -179,10 +183,10 @@ public class FancyStatusBars extends DrawableHelper {
final int[] offsets = new int[]{-1, 1};
for (int i : offsets) {
- textRenderer.draw(matrices, text, (float) (x + i), (float) y, 0);
- textRenderer.draw(matrices, text, (float) x, (float) (y + i), 0);
+ context.drawText(textRenderer, text, x + i, y, 0, false);
+ context.drawText(textRenderer, text, x, y + i, 0, false);
}
- textRenderer.draw(matrices, text, (float) x, (float) y, text_color);
+ context.drawText(textRenderer, text, x, y, text_color, false);
}
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java
new file mode 100644
index 00000000..8dee3fcb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java
@@ -0,0 +1,62 @@
+package me.xmrvizzy.skyblocker.skyblock;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.title.Title;
+import net.fabricmc.fabric.api.event.player.UseItemCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.FishingRodItem;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.TypedActionResult;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.math.Vec3d;
+
+public class FishingHelper {
+ private static final Title title = new Title("skyblocker.fishing.reelNow", Formatting.GREEN);
+ private static long startTime;
+ private static Vec3d normalYawVector;
+
+ public static void init() {
+ UseItemCallback.EVENT.register((player, world, hand) -> {
+ ItemStack stack = player.getStackInHand(hand);
+ if (stack.getItem() instanceof FishingRodItem) {
+ if (player.fishHook == null) {
+ start(player);
+ } else {
+ reset();
+ }
+ }
+ return TypedActionResult.pass(stack);
+ });
+ }
+
+ public static void start(PlayerEntity player) {
+ startTime = System.currentTimeMillis();
+ float yawRad = player.getYaw() * 0.017453292F;
+ normalYawVector = new Vec3d(-MathHelper.sin(yawRad), 0, MathHelper.cos(yawRad));
+ }
+
+ public static void reset() {
+ startTime = 0;
+ }
+
+ public static void onSound(MinecraftClient client, PlaySoundS2CPacket packet) {
+ String path = packet.getSound().value().getId().getPath();
+ if (SkyblockerConfig.get().general.fishing.enableFishingHelper && startTime != 0 && System.currentTimeMillis() >= startTime + 2000 && ("entity.generic.splash".equals(path) || "entity.player.splash".equals(path))) {
+ ClientPlayerEntity player = client.player;
+ if (player != null && player.fishHook != null) {
+ Vec3d soundToFishHook = player.fishHook.getPos().subtract(packet.getX(), 0, packet.getZ());
+ if (Math.abs(normalYawVector.x * soundToFishHook.z - normalYawVector.z * soundToFishHook.x) < 0.2D && Math.abs(normalYawVector.dotProduct(soundToFishHook)) < 4D && player.getPos().squaredDistanceTo(packet.getX(), packet.getY(), packet.getZ()) > 1D) {
+ RenderHelper.displayInTitleContainerAndPlaySound(title, 10);
+ reset();
+ }
+ } else {
+ reset();
+ }
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
index c536542b..2d868782 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
@@ -1,8 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.container.ColorHighlight;
-import me.xmrvizzy.skyblocker.container.ContainerSolver;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import me.xmrvizzy.skyblocker.gui.ContainerSolver;
import net.minecraft.item.ItemStack;
import java.util.ArrayList;
@@ -11,20 +11,23 @@ import java.util.Map;
public class CroesusHelper extends ContainerSolver {
- public CroesusHelper() { super("^Croesus$"); }
+ public CroesusHelper() {
+ super("^Croesus$");
+ }
@Override
- public boolean isEnabled() {
+ protected boolean isEnabled() {
return SkyblockerConfig.get().locations.dungeons.croesusHelper;
}
@Override
- public List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
List<ColorHighlight> highlights = new ArrayList<>();
for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {
ItemStack stack = entry.getValue();
- if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("opened"))
- highlights.add(new ColorHighlight(entry.getKey(), GRAY_HIGHLIGHT));
+ if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("opened")) {
+ highlights.add(ColorHighlight.gray(entry.getKey()));
+ }
}
return highlights;
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
index 4554372b..219f4258 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
@@ -21,7 +21,7 @@ public class DungeonBlaze {
public static void update() {
ClientWorld world = MinecraftClient.getInstance().world;
- if (world == null || !Utils.isInDungeons) return;
+ if (world == null || !Utils.isInDungeons()) return;
if(!renderHooked){
WorldRenderEvents.END.register(DungeonBlaze::blazeRenderer);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java
index dda8da41..2a97d0ee 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java
@@ -2,8 +2,13 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon;
import org.apache.commons.lang3.StringUtils;
+import com.mojang.brigadier.Command;
+
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.MapRenderer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.util.math.MatrixStack;
@@ -11,8 +16,11 @@ import net.minecraft.item.FilledMapItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.map.MapState;
import net.minecraft.nbt.NbtCompound;
+import net.minecraft.text.Text;
+import net.minecraft.util.Identifier;
public class DungeonMap {
+ private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png");
public static void render(MatrixStack matrices) {
MinecraftClient client = MinecraftClient.getInstance();
@@ -28,14 +36,36 @@ public class DungeonMap {
MapRenderer map = client.gameRenderer.getMapRenderer();
MapState state = FilledMapItem.getMapState(mapid, client.world);
float scaling = SkyblockerConfig.get().locations.dungeons.mapScaling;
+ int x = SkyblockerConfig.get().locations.dungeons.mapX;
+ int y = SkyblockerConfig.get().locations.dungeons.mapY;
if (state == null) return;
matrices.push();
- matrices.translate(2, 2, 0);
+ matrices.translate(x, y, 0);
matrices.scale(scaling, scaling, 0f);
map.draw( matrices, vertices, mapid, state, false, 15728880);
vertices.draw();
matrices.pop();
}
}
+
+ public static void renderHUDMap(DrawContext context, int x, int y) {
+ float scaling = SkyblockerConfig.get().locations.dungeons.mapScaling;
+ int size = (int) (128 * scaling);
+ context.drawTexture(MAP_BACKGROUND, x, y, 0, 0, size, size, size, size);
+ }
+
+ public static void init() {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
+ dispatcher.register(ClientCommandManager.literal("skyblocker")
+ .then(ClientCommandManager.literal("hud")
+ .then(ClientCommandManager.literal("dungeonmap")
+ .executes(context -> {
+ MinecraftClient client = context.getSource().getClient();
+ client.send(() -> client.setScreen(new DungeonMapConfigScreen(Text.literal("Dungeon Map Config"))));
+
+ return Command.SINGLE_SUCCESS;
+ }))));
+ });
+ }
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
new file mode 100644
index 00000000..ffd7a8b6
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
@@ -0,0 +1,57 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import java.awt.Color;
+
+import me.shedaniel.autoconfig.AutoConfig;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderUtils;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+
+public class DungeonMapConfigScreen extends Screen {
+
+ private int hudX = SkyblockerConfig.get().locations.dungeons.mapX;
+ private int hudY = SkyblockerConfig.get().locations.dungeons.mapY;
+
+ protected DungeonMapConfigScreen(Text title) {
+ super(title);
+ }
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ renderBackground(context);
+ DungeonMap.renderHUDMap(context, hudX, hudY);
+ context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB());
+ }
+
+ @Override
+ public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
+ float scaling = SkyblockerConfig.get().locations.dungeons.mapScaling;
+ int size = (int) (128 * scaling);
+ if(RenderUtils.pointExistsInArea((int) mouseX, (int) mouseY, hudX, hudY, hudX + size, hudY + size) && button == 0) {
+ hudX = (int) Math.max(Math.min(mouseX - (size / 2), this.width - size), 0);
+ hudY = (int) Math.max(Math.min(mouseY - (size / 2), this.height - size), 0);
+ }
+ return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if(button == 1) {
+ hudX = 2;
+ hudY = 2;
+ }
+
+ return super.mouseClicked(mouseX, mouseY, button);
+ }
+
+ @Override
+ public void close() {
+ SkyblockerConfig.get().locations.dungeons.mapX = hudX;
+ SkyblockerConfig.get().locations.dungeons.mapY = hudY;
+ AutoConfig.getConfigHolder(SkyblockerConfig.class).save();
+ super.close();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
new file mode 100644
index 00000000..4701c485
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
@@ -0,0 +1,42 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.util.math.BlockPos;
+
+public class LividColor {
+ private static int tenTicks = 0;
+
+ public static void init() {
+ ClientReceiveMessageEvents.GAME.register((message, overlay) -> {
+ if (SkyblockerConfig.get().locations.dungeons.lividColor.enableLividColor && message.getString().equals("[BOSS] Livid: I respect you for making it to here, but I'll be your undoing.")) {
+ tenTicks = 8;
+ }
+ });
+ }
+
+ public static void update() {
+ MinecraftClient client = MinecraftClient.getInstance();
+ if (tenTicks != 0) {
+ if (SkyblockerConfig.get().locations.dungeons.lividColor.enableLividColor && Utils.isInDungeons() && client.world != null) {
+ if (tenTicks == 1) {
+ SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown(SkyblockerConfig.get().locations.dungeons.lividColor.lividColorText.replace("[color]", "red"));
+ tenTicks = 0;
+ return;
+ }
+ String key = client.world.getBlockState(new BlockPos(5, 110, 42)).getBlock().getTranslationKey();
+ if (key.startsWith("block.minecraft.") && key.endsWith("wool") && !key.endsWith("red_wool")) {
+ SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown(SkyblockerConfig.get().locations.dungeons.lividColor.lividColorText.replace("[color]", key.substring(16, key.length() - 5)));
+ tenTicks = 0;
+ return;
+ }
+ tenTicks--;
+ } else {
+ tenTicks = 0;
+ }
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
index 42fcc36a..8ae7ce7b 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
@@ -3,6 +3,7 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon;
import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.chat.ChatFilterResult;
import me.xmrvizzy.skyblocker.chat.ChatPatternListener;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -15,45 +16,60 @@ import java.util.regex.Pattern;
public class Reparty extends ChatPatternListener {
private static final MinecraftClient client = MinecraftClient.getInstance();
- private static final SkyblockerMod skyblocker = SkyblockerMod.getInstance();
public static final Pattern PLAYER = Pattern.compile(" ([a-zA-Z0-9_]{2,16}) ●");
private static final int BASE_DELAY = 10;
private String[] players;
private int playersSoFar;
private boolean repartying;
+ private String partyLeader;
public Reparty() {
- super("^(?:You are not currently in a party\\.|Party (?:Membe|Moderato)rs(?: \\(([0-9]+)\\)|:( .*)))$");
+ super("^(?:You are not currently in a party\\." +
+ "|Party (?:Membe|Moderato)rs(?: \\(([0-9]+)\\)|:( .*))" +
+ "|([\\[A-z+\\]]* )?(?<disband>.*) has disbanded .*" +
+ "|.*\n([\\[A-z+\\]]* )?(?<invite>.*) has invited you to join their party!" +
+ "\nYou have 60 seconds to accept. Click here to join!\n.*)$");
+
this.repartying = false;
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("rp").executes(context -> {
- if (!Utils.isOnSkyblock || this.repartying || client.player == null) return 0;
+ if (!Utils.isOnSkyblock() || this.repartying || client.player == null) return 0;
this.repartying = true;
- client.player.networkHandler.sendCommand("p list");
+ SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown("/p list");
return 0;
})));
}
@Override
public ChatFilterResult state() {
- return this.repartying ? ChatFilterResult.FILTER : ChatFilterResult.PASS;
+ return (SkyblockerConfig.get().general.acceptReparty || this.repartying) ? ChatFilterResult.FILTER : ChatFilterResult.PASS;
}
@Override
public boolean onMatch(Text message, Matcher matcher) {
- if (matcher.group(1) != null) {
+ if (matcher.group(1) != null && repartying) {
this.playersSoFar = 0;
this.players = new String[Integer.parseInt(matcher.group(1)) - 1];
- } else if (matcher.group(2) != null) {
+ } else if (matcher.group(2) != null && repartying) {
Matcher m = PLAYER.matcher(matcher.group(2));
while (m.find()) {
this.players[playersSoFar++] = m.group(1);
}
+ } else if (matcher.group("disband") != null && !matcher.group("disband").equals(client.getSession().getUsername())) {
+ partyLeader = matcher.group("disband");
+ SkyblockerMod.getInstance().scheduler.schedule(() -> partyLeader = null, 61);
+ return false;
+ } else if (matcher.group("invite") != null && matcher.group("invite").equals(partyLeader)) {
+ String command = "/party accept " + partyLeader;
+ sendCommand(command, 0);
+ return false;
} else {
this.repartying = false;
return false;
}
- if (this.playersSoFar == this.players.length) reparty();
+ if (this.playersSoFar == this.players.length) {
+ reparty();
+ }
return false;
}
@@ -63,16 +79,15 @@ public class Reparty extends ChatPatternListener {
this.repartying = false;
return;
}
- sendCommand(playerEntity, "p disband", 1);
+ sendCommand("/p disband", 1);
for (int i = 0; i < this.players.length; ++i) {
- String command = "p invite " + this.players[i];
- sendCommand(playerEntity, command, i + 2);
+ String command = "/p invite " + this.players[i];
+ sendCommand(command, i + 2);
}
- skyblocker.scheduler.schedule(() -> this.repartying = false, this.players.length + 2);
+ SkyblockerMod.getInstance().scheduler.schedule(() -> this.repartying = false, this.players.length + 2);
}
- private void sendCommand(ClientPlayerEntity player, String command, int delay) {
- // skyblocker.scheduler.schedule(() -> player.sendChatMessage(command, Text.of(command)), delay * BASE_DELAY);
- skyblocker.scheduler.schedule(() -> player.networkHandler.sendCommand(command), delay * BASE_DELAY);
+ private void sendCommand(String command, int delay) {
+ SkyblockerMod.getInstance().messageScheduler.queueMessage(command, delay * BASE_DELAY);
}
-}
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java
index 10a2e413..f7598af5 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java
@@ -69,7 +69,7 @@ public class Trivia extends ChatPatternListener {
answers.put("What is the status of Storm?", new String[]{"The Wither Lords"});
answers.put("What is the status of Necron?", new String[]{"The Wither Lords"});
answers.put("What is the status of Maxor, Storm, Goldor and Necron?", new String[]{"The Wither Lords"});
- answers.put("How many total Fairy Souls are there?", new String[]{"240 Fairy Souls"});
+ answers.put("How many total Fairy Souls are there?", new String[]{"242 Fairy Souls"});
answers.put("How many Fairy Souls are there in Spider's Den?", new String[]{"19 Fairy Souls"});
answers.put("How many Fairy Souls are there in The End?", new String[]{"12 Fairy Souls"});
answers.put("How many Fairy Souls are there in The Farming Islands?", new String[]{"20 Fairy Souls"});
@@ -87,7 +87,7 @@ public class Trivia extends ChatPatternListener {
answers.put("What is the name of the person that upgrades pets?", new String[]{"Kat"});
answers.put("What is the name of the lady of the Nether?", new String[]{"Elle"});
answers.put("Which villager in the Village gives you a Rogue Sword?", new String[]{"Jamie"});
- answers.put("How many unique minions are there?", new String[]{"58 Minions"});
+ answers.put("How many unique minions are there?", new String[]{"59 Minions"});
answers.put("Which of these enemies does not spawn in the Spider's Den?", new String[]{"Zombie Spider", "Cave Spider", "Wither Skeleton",
"Dashing Spooder", "Broodfather", "Night Spider"});
answers.put("Which of these monsters only spawns at night?", new String[]{"Zombie Villager", "Ghast"});
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java
index f5c97738..add30907 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java
@@ -1,12 +1,11 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.container.ColorHighlight;
-import me.xmrvizzy.skyblocker.container.ContainerSolver;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import me.xmrvizzy.skyblocker.gui.ContainerSolver;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
-// import net.minecraft.registry.Registry;
import net.minecraft.registry.Registries;
import net.minecraft.util.DyeColor;
import net.minecraft.util.Identifier;
@@ -27,27 +26,28 @@ public class ColorTerminal extends ContainerSolver {
}
@Override
- public boolean isEnabled() {
+ protected boolean isEnabled() {
targetColor = null;
return SkyblockerConfig.get().locations.dungeons.terminals.solveColor;
}
@Override
- public List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
trimEdges(slots, 6);
List<ColorHighlight> highlights = new ArrayList<>();
String colorString = groups[0];
- if(targetColor == null) {
+ if (targetColor == null) {
targetColor = colorFromName.get(colorString);
- if(targetColor == null) {
+ if (targetColor == null) {
LOGGER.error("[Skyblocker] Couldn't find dye color corresponding to \"" + colorString + "\"");
return Collections.emptyList();
}
}
- for(Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
+ for (Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
ItemStack itemStack = slot.getValue();
- if(!itemStack.hasEnchantments() && targetColor.equals(itemColor.get(itemStack.getItem())))
- highlights.add(new ColorHighlight(slot.getKey(), GREEN_HIGHLIGHT));
+ if (!itemStack.hasEnchantments() && targetColor.equals(itemColor.get(itemStack.getItem()))) {
+ highlights.add(ColorHighlight.green(slot.getKey()));
+ }
}
return highlights;
}
@@ -63,7 +63,6 @@ public class ColorTerminal extends ContainerSolver {
itemColor = new HashMap<>();
for (DyeColor color : DyeColor.values())
for (String item : new String[]{"dye", "wool", "stained_glass", "terracotta"})
- // itemColor.put(Registry.ITEM.get(new Identifier(color.getName() + '_' + item)), color);
itemColor.put(Registries.ITEM.get(new Identifier(color.getName() + '_' + item)), color);
itemColor.put(Items.BONE_MEAL, DyeColor.WHITE);
itemColor.put(Items.LAPIS_LAZULI, DyeColor.BLUE);
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java
index c61395f4..3b4e1c50 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java
@@ -1,8 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.container.ColorHighlight;
-import me.xmrvizzy.skyblocker.container.ContainerSolver;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import me.xmrvizzy.skyblocker.gui.ContainerSolver;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
@@ -21,14 +21,14 @@ public class OrderTerminal extends ContainerSolver {
}
@Override
- public boolean isEnabled() {
+ protected boolean isEnabled() {
orderedSlots = null;
currentNum = 0;
return SkyblockerConfig.get().locations.dungeons.terminals.solveOrder;
}
@Override
- public List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
if(orderedSlots == null && !orderSlots(slots))
return Collections.emptyList();
while(currentNum < PANES_NUM && Items.LIME_STAINED_GLASS_PANE.equals(slots.get(orderedSlots[currentNum]).getItem()))
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java
index 7c56746f..18326254 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java
@@ -1,8 +1,8 @@
package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.container.ColorHighlight;
-import me.xmrvizzy.skyblocker.container.ContainerSolver;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import me.xmrvizzy.skyblocker.gui.ContainerSolver;
import net.minecraft.item.ItemStack;
import java.util.ArrayList;
@@ -15,19 +15,20 @@ public class StartsWithTerminal extends ContainerSolver {
}
@Override
- public boolean isEnabled() {
+ protected boolean isEnabled() {
return SkyblockerConfig.get().locations.dungeons.terminals.solveStartsWith;
}
@Override
- public List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
trimEdges(slots, 6);
String prefix = groups[0];
List<ColorHighlight> highlights = new ArrayList<>();
- for(Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
+ for (Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
ItemStack stack = slot.getValue();
- if(!stack.hasEnchantments() && stack.getName().getString().startsWith(prefix))
- highlights.add(new ColorHighlight(slot.getKey(), GREEN_HIGHLIGHT));
+ if (!stack.hasEnchantments() && stack.getName().getString().startsWith(prefix)) {
+ highlights.add(ColorHighlight.green(slot.getKey()));
+ }
}
return highlights;
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
index 86fe58fe..122f6c6c 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
@@ -1,11 +1,12 @@
package me.xmrvizzy.skyblocker.skyblock.dwarven;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CommsWidget;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
@@ -48,24 +49,58 @@ public class DwarvenHud {
return 1;
})))));
- HudRenderCallback.EVENT.register((matrixStack, tickDelta) -> {
- if (!SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled || client.player == null || commissionList.isEmpty()) return;
- render(matrixStack, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.x, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.y, commissionList);
+ HudRenderCallback.EVENT.register((context, tickDelta) -> {
+ if (!SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled
+ || client.options.playerListKey.isPressed()
+ || client.player == null
+ || commissionList.isEmpty()) {
+ return;
+ }
+ render(context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.x, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.y, commissionList);
});
}
- public static void render(MatrixStack matrixStack, int hudX, int hudY, List<Commission> commissions) {
- if (commissions.size() > 0){
- if (SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground)
- DrawableHelper.fill(matrixStack, hudX, hudY, hudX + 200, hudY + (20 * commissions.size()), 0x64000000);
- int y = 0;
- for (Commission commission : commissions) {
- client.textRenderer.drawWithShadow(matrixStack, Text.literal(commission.commission).styled(style -> style.withColor(Formatting.AQUA)).append(Text.literal(": " + commission.progression).styled(style -> style.withColor(Formatting.GREEN))), hudX + 5, hudY + y + 5, 0xFFFFFFFF);
- y += 20;
- }
+ public static void render(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+
+ switch(SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.style) {
+ case SIMPLE -> renderSimple(context, hudX, hudY, commissions);
+ case FANCY -> renderFancy(context, hudX, hudY, commissions);
+ case CLASSIC -> renderClassic(context, hudX, hudY, commissions);
+ }
+ }
+
+ public static void renderClassic(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+ if (SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground) {
+ context.fill(hudX, hudY, hudX + 200, hudY + (20 * commissions.size()), 0x64000000);
+ }
+
+ int y = 0;
+ for (Commission commission : commissions) {
+ context
+ .drawTextWithShadow(client.textRenderer,
+ Text.literal(commission.commission + ": ")
+ .styled(style -> style.withColor(Formatting.AQUA))
+ .append(Text.literal(commission.progression)
+ .styled(style -> style.withColor(Formatting.GREEN))),
+ hudX + 5, hudY + y + 5, 0xFFFFFFFF);
+ y += 20;
}
}
+ public static void renderSimple(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+ CommsWidget cw = new CommsWidget(commissions, false);
+ cw.setX(hudX);
+ cw.setY(hudY);
+ cw.render(context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
+ }
+
+ public static void renderFancy(DrawContext context, int hudX, int hudY, List<Commission> commissions) {
+ CommsWidget cw = new CommsWidget(commissions, true);
+ cw.setX(hudX);
+ cw.setY(hudY);
+ cw.render(context, SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enableBackground);
+ }
+
public static void update() {
commissionList = new ArrayList<>();
if (client.player == null || !SkyblockerConfig.get().locations.dwarvenMines.dwarvenHud.enabled) return;
@@ -83,13 +118,7 @@ public class DwarvenHud {
});
}
- public static class Commission{
- final String commission;
- final String progression;
+ // steamroller tactics to get visibility from outside classes (CommsWidget)
+ public static record Commission(String commission, String progression){}
- public Commission(String commission, String progression){
- this.commission = commission;
- this.progression = progression;
- }
- }
-}
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
index 2d0ba892..f91ed921 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
@@ -3,6 +3,7 @@ package me.xmrvizzy.skyblocker.skyblock.dwarven;
import me.shedaniel.autoconfig.AutoConfig;
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
import me.xmrvizzy.skyblocker.utils.RenderUtils;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
@@ -19,11 +20,11 @@ public class DwarvenHudConfigScreen extends Screen {
}
@Override
- public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
- super.render(matrices, mouseX, mouseY, delta);
- renderBackground(matrices);
- DwarvenHud.render(matrices, hudX, hudY, List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")));
- drawCenteredTextWithShadow(matrices, textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB());
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ renderBackground(context);
+ DwarvenHud.render(context, hudX, hudY, List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")));
+ context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB());
}
@Override
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java
new file mode 100644
index 00000000..40793169
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java
@@ -0,0 +1,128 @@
+package me.xmrvizzy.skyblocker.skyblock.experiment;
+
+import com.google.common.collect.ImmutableMap;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class ChronomatronSolver extends ExperimentSolver {
+ public static final ImmutableMap<Item, Item> TERRACOTTA_TO_GLASS = ImmutableMap.ofEntries(
+ new AbstractMap.SimpleImmutableEntry<>(Items.RED_TERRACOTTA, Items.RED_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.ORANGE_TERRACOTTA, Items.ORANGE_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.YELLOW_TERRACOTTA, Items.YELLOW_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.LIME_TERRACOTTA, Items.LIME_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.GREEN_TERRACOTTA, Items.GREEN_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.CYAN_TERRACOTTA, Items.CYAN_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.LIGHT_BLUE_TERRACOTTA, Items.LIGHT_BLUE_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.BLUE_TERRACOTTA, Items.BLUE_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.PURPLE_TERRACOTTA, Items.PURPLE_STAINED_GLASS),
+ new AbstractMap.SimpleImmutableEntry<>(Items.PINK_TERRACOTTA, Items.PINK_STAINED_GLASS)
+ );
+
+ private final List<Item> chronomatronSlots = new ArrayList<>();
+ private int chronomatronChainLengthCount;
+ private int chronomatronCurrentSlot;
+ private int chronomatronCurrentOrdinal;
+
+ public ChronomatronSolver() {
+ super("^Chronomatron \\(\\w+\\)$");
+ }
+
+ public List<Item> getChronomatronSlots() {
+ return chronomatronSlots;
+ }
+
+ public int getChronomatronCurrentOrdinal() {
+ return chronomatronCurrentOrdinal;
+ }
+
+ public int incrementChronomatronCurrentOrdinal() {
+ return ++chronomatronCurrentOrdinal;
+ }
+
+ @Override
+ protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) {
+ return experimentsConfig.enableChronomatronSolver;
+ }
+
+ @Override
+ protected void tick(Screen screen) {
+ if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Chronomatron (")) {
+ switch (getState()) {
+ case REMEMBER -> {
+ Inventory inventory = genericContainerScreen.getScreenHandler().getInventory();
+ if (chronomatronCurrentSlot == 0) {
+ for (int index = 10; index < 43; index++) {
+ if (inventory.getStack(index).hasEnchantments()) {
+ if (chronomatronSlots.size() <= chronomatronChainLengthCount) {
+ chronomatronSlots.add(TERRACOTTA_TO_GLASS.get(inventory.getStack(index).getItem()));
+ setState(State.WAIT);
+ } else {
+ chronomatronChainLengthCount++;
+ }
+ chronomatronCurrentSlot = index;
+ return;
+ }
+ }
+ } else if (!inventory.getStack(chronomatronCurrentSlot).hasEnchantments()) {
+ chronomatronCurrentSlot = 0;
+ }
+ }
+ case WAIT -> {
+ if (genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString().startsWith("Timer: ")) {
+ setState(State.SHOW);
+ }
+ }
+ case END -> {
+ String name = genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString();
+ if (!name.startsWith("Timer: ")) {
+ if (name.equals("Remember the pattern!")) {
+ chronomatronChainLengthCount = 0;
+ chronomatronCurrentOrdinal = 0;
+ setState(State.REMEMBER);
+ } else {
+ reset();
+ }
+ }
+ }
+ }
+ } else {
+ reset();
+ }
+ }
+
+ @Override
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
+ List<ColorHighlight> highlights = new ArrayList<>();
+ if (getState() == State.SHOW && chronomatronSlots.size() > chronomatronCurrentOrdinal) {
+ for (Map.Entry<Integer, ItemStack> indexStack : slots.entrySet()) {
+ int index = indexStack.getKey();
+ ItemStack stack = indexStack.getValue();
+ Item item = chronomatronSlots.get(chronomatronCurrentOrdinal);
+ if (stack.isOf(item) || TERRACOTTA_TO_GLASS.get(stack.getItem()) == item) {
+ highlights.add(ColorHighlight.green(index));
+ }
+ }
+ }
+ return highlights;
+ }
+
+ @Override
+ protected void reset() {
+ super.reset();
+ chronomatronSlots.clear();
+ chronomatronChainLengthCount = 0;
+ chronomatronCurrentSlot = 0;
+ chronomatronCurrentOrdinal = 0;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java
new file mode 100644
index 00000000..5dad908e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java
@@ -0,0 +1,59 @@
+package me.xmrvizzy.skyblocker.skyblock.experiment;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.gui.ContainerSolver;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.item.ItemStack;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class ExperimentSolver extends ContainerSolver {
+ public enum State {
+ REMEMBER, WAIT, SHOW, END
+ }
+
+ private State state = State.REMEMBER;
+ private final Map<Integer, ItemStack> slots = new HashMap<>();
+
+ protected ExperimentSolver(String containerName) {
+ super(containerName);
+ }
+
+ public State getState() {
+ return state;
+ }
+
+ public void setState(State state) {
+ this.state = state;
+ }
+
+ public Map<Integer, ItemStack> getSlots() {
+ return slots;
+ }
+
+ @Override
+ protected final boolean isEnabled() {
+ return isEnabled(SkyblockerConfig.get().general.experiments);
+ }
+
+ protected abstract boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig);
+
+ @Override
+ protected void start(GenericContainerScreen screen) {
+ super.start(screen);
+ state = State.REMEMBER;
+ ScreenEvents.afterTick(screen).register(this::tick);
+ }
+
+ @Override
+ protected void reset() {
+ super.reset();
+ state = State.REMEMBER;
+ slots.clear();
+ }
+
+ protected abstract void tick(Screen screen);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java
new file mode 100644
index 00000000..9f6e8b2a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java
@@ -0,0 +1,81 @@
+package me.xmrvizzy.skyblocker.skyblock.experiment;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+
+import java.util.*;
+
+public class SuperpairsSolver extends ExperimentSolver {
+ private int superpairsPrevClickedSlot;
+ private ItemStack superpairsCurrentSlot;
+ private final Set<Integer> superpairsDuplicatedSlots = new HashSet<>();
+
+ public SuperpairsSolver() {
+ super("^Superpairs \\(\\w+\\)$");
+ }
+
+ public void setSuperpairsPrevClickedSlot(int superpairsPrevClickedSlot) {
+ this.superpairsPrevClickedSlot = superpairsPrevClickedSlot;
+ }
+
+ public void setSuperpairsCurrentSlot(ItemStack superpairsCurrentSlot) {
+ this.superpairsCurrentSlot = superpairsCurrentSlot;
+ }
+
+ @Override
+ protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) {
+ return experimentsConfig.enableSuperpairsSolver;
+ }
+
+ @Override
+ protected void start(GenericContainerScreen screen) {
+ super.start(screen);
+ setState(State.SHOW);
+ }
+
+ @Override
+ protected void tick(Screen screen) {
+ if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Superpairs (")) {
+ if (getState() == State.SHOW) {
+ if (genericContainerScreen.getScreenHandler().getInventory().getStack(4).isOf(Items.CAULDRON)) {
+ reset();
+ } else if (getSlots().get(superpairsPrevClickedSlot) == null) {
+ ItemStack itemStack = genericContainerScreen.getScreenHandler().getInventory().getStack(superpairsPrevClickedSlot);
+ if (!(itemStack.isOf(Items.CYAN_STAINED_GLASS) || itemStack.isOf(Items.BLACK_STAINED_GLASS_PANE) || itemStack.isOf(Items.AIR))) {
+ getSlots().entrySet().stream().filter((entry -> ItemStack.areEqual(entry.getValue(), itemStack))).findAny().ifPresent(entry -> superpairsDuplicatedSlots.add(entry.getKey()));
+ getSlots().put(superpairsPrevClickedSlot, itemStack);
+ superpairsCurrentSlot = itemStack;
+ }
+ }
+ }
+ } else {
+ reset();
+ }
+ }
+
+ @Override
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> displaySlots) {
+ List<ColorHighlight> highlights = new ArrayList<>();
+ if (getState() == State.SHOW) {
+ for (Map.Entry<Integer, ItemStack> indexStack : displaySlots.entrySet()) {
+ int index = indexStack.getKey();
+ ItemStack displayStack = indexStack.getValue();
+ ItemStack stack = getSlots().get(index);
+ if (stack != null && !ItemStack.areEqual(stack, displayStack)) {
+ if (ItemStack.areEqual(superpairsCurrentSlot, stack) && displayStack.getName().getString().equals("Click a second button!")) {
+ highlights.add(ColorHighlight.green(index));
+ } else if (superpairsDuplicatedSlots.contains(index)) {
+ highlights.add(ColorHighlight.yellow(index));
+ } else {
+ highlights.add(ColorHighlight.red(index));
+ }
+ }
+ }
+ }
+ return highlights;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java
new file mode 100644
index 00000000..45d6c0c6
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java
@@ -0,0 +1,80 @@
+package me.xmrvizzy.skyblocker.skyblock.experiment;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.gui.ColorHighlight;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.item.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class UltrasequencerSolver extends ExperimentSolver {
+ private int ultrasequencerNextSlot;
+
+ public UltrasequencerSolver() {
+ super("^Ultrasequencer \\(\\w+\\)$");
+ }
+
+ public int getUltrasequencerNextSlot() {
+ return ultrasequencerNextSlot;
+ }
+
+ public void setUltrasequencerNextSlot(int ultrasequencerNextSlot) {
+ this.ultrasequencerNextSlot = ultrasequencerNextSlot;
+ }
+
+ @Override
+ protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) {
+ return experimentsConfig.enableUltrasequencerSolver;
+ }
+
+ @Override
+ protected void tick(Screen screen) {
+ if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Ultrasequencer (")) {
+ switch (getState()) {
+ case REMEMBER -> {
+ Inventory inventory = genericContainerScreen.getScreenHandler().getInventory();
+ if (inventory.getStack(49).getName().getString().equals("Remember the pattern!")) {
+ for (int index = 9; index < 45; index++) {
+ ItemStack itemStack = inventory.getStack(index);
+ String name = itemStack.getName().getString();
+ if (name.matches("\\d+")) {
+ if (name.equals("1")) {
+ ultrasequencerNextSlot = index;
+ }
+ getSlots().put(index, itemStack);
+ }
+ }
+ setState(State.WAIT);
+ }
+ }
+ case WAIT -> {
+ if (genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString().startsWith("Timer: ")) {
+ setState(State.SHOW);
+ }
+ }
+ case END -> {
+ String name = genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString();
+ if (!name.startsWith("Timer: ")) {
+ if (name.equals("Remember the pattern!")) {
+ getSlots().clear();
+ setState(State.REMEMBER);
+ } else {
+ reset();
+ }
+ }
+ }
+ }
+ } else {
+ reset();
+ }
+ }
+
+ @Override
+ protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
+ return getState() == State.SHOW && ultrasequencerNextSlot != 0 ? List.of(ColorHighlight.green(ultrasequencerNextSlot)) : new ArrayList<>();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
index 8d8c86b4..dc2a89f8 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java
@@ -23,8 +23,10 @@ import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.zip.GZIPInputStream;
@@ -38,40 +40,48 @@ public class PriceInfoTooltip {
private static JsonObject threeDayAvgPricesJson;
private static JsonObject lowestPricesJson;
private static JsonObject isMuseumJson;
+ private static JsonObject motesPricesJson;
private static boolean nullMsgSend = false;
private final static Gson gson = new Gson();
+ private static final Map<String, String> apiAddresses;
public static void onInjectTooltip(ItemStack stack, TooltipContext context, List<Text> lines) {
- if (!Utils.isOnSkyblock || client.player == null) return;
+ if (!Utils.isOnSkyblock() || client.player == null) return;
String name = getInternalNameFromNBT(stack);
if (name == null) return;
int count = stack.getCount();
- String timestamp = getTimestamp(stack);
boolean bazaarOpened = lines.stream().anyMatch(each -> each.getString().contains("Buy price:") || each.getString().contains("Sell price:"));
if (SkyblockerConfig.get().general.itemTooltip.enableNPCPrice) {
if (npcPricesJson == null) {
- if (!nullMsgSend) {
- client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
- nullMsgSend = true;
- }
- } else if (npcPricesJson.has(name)) {
+ nullWarning();
+ }
+ else if (npcPricesJson.has(name)) {
lines.add(Text.literal(String.format("%-21s", "NPC Price:"))
.formatted(Formatting.YELLOW)
.append(getCoinsMessage(npcPricesJson.get(name).getAsDouble(), count)));
}
}
+
+ if (SkyblockerConfig.get().general.itemTooltip.enableMotesPrice && Utils.isInTheRift()) {
+ if(motesPricesJson == null) {
+ nullWarning();
+ }
+ else if (motesPricesJson.has(name)) {
+ lines.add(Text.literal(String.format("%-20s", "Motes Price:"))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(getMotesMessage(motesPricesJson.get(name).getAsInt(), count)));
+ }
+ }
boolean bazaarExist = false;
if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice && !bazaarOpened) {
if (bazaarPricesJson == null) {
- if (!nullMsgSend) {
- client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
- nullMsgSend = true;
- }
- } else if (bazaarPricesJson.has(name)) {
+ nullWarning();
+ }
+ else if (bazaarPricesJson.has(name)) {
JsonObject getItem = bazaarPricesJson.getAsJsonObject(name);
lines.add(Text.literal(String.format("%-18s", "Bazaar buy Price:"))
.formatted(Formatting.GOLD)
@@ -88,101 +98,115 @@ public class PriceInfoTooltip {
}
// bazaarOpened & bazaarExist check for lbin, because Skytils keeps some bazaar item data in lbin api
+ boolean lbinExist = false;
if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN && !bazaarOpened && !bazaarExist) {
if (lowestPricesJson == null) {
- if (!nullMsgSend) {
- client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
- nullMsgSend = true;
- }
- } else if (lowestPricesJson.has(name)) {
+ nullWarning();
+ }
+ else if (lowestPricesJson.has(name)) {
lines.add(Text.literal(String.format("%-19s", "Lowest BIN Price:"))
.formatted(Formatting.GOLD)
.append(getCoinsMessage(lowestPricesJson.get(name).getAsDouble(), count)));
+ lbinExist = true;
}
}
if (SkyblockerConfig.get().general.itemTooltip.enableAvgBIN) {
if (threeDayAvgPricesJson == null || oneDayAvgPricesJson == null) {
- if (!nullMsgSend) {
- client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
- nullMsgSend = true;
- }
- } else if (threeDayAvgPricesJson.has(name) || oneDayAvgPricesJson.has(name)) {
+ nullWarning();
+ }
+ else {
/*
- We are skipping check average prices for potions and runes
- because there is no data for their in API.
+ We are skipping check average prices for potions, runes
+ and enchanted books because there is no data for their in API.
*/
if (name.contains("PET-")) {
name = name.replace("PET-", "")
- .replace("COMMON", "0")
.replace("UNCOMMON", "1")
+ .replace("COMMON", "0")
.replace("RARE", "2")
.replace("EPIC", "3")
.replace("LEGENDARY", "4")
.replace("MYTHIC", "5")
.replace("-", ";");
- } else if (name.contains("ENCHANTED_BOOK-")) {
- name = name.replace("ENCHANTED_BOOK-", "").replace("-", ";");
- } else if (name.contains("POTION-")) {
- name = "";
} else if (name.contains("RUNE-")) {
+ name = name.replace("RUNE-", "");
+ name = name.substring(0, name.indexOf("-")) + "_RUNE;" + name.substring(name.lastIndexOf("-") + 1);
+ } else if (name.contains("POTION-") || name.contains("ENCHANTED_BOOK-")) {
name = "";
} else {
name = name.replace(":", "-");
}
- SkyblockerConfig.Average type = SkyblockerConfig.get().general.itemTooltip.avg;
+ if (!name.isEmpty() && lbinExist) {
+ SkyblockerConfig.Average type = SkyblockerConfig.get().general.itemTooltip.avg;
- // "No data" line because of API not keeping old data, it causes NullPointerException
- if (!name.isEmpty() && (type == SkyblockerConfig.Average.ONE_DAY || type == SkyblockerConfig.Average.BOTH)) {
- lines.add(Text.literal(String.format("%-19s", "1 Day Avg. Price:"))
- .formatted(Formatting.GOLD)
- .append(oneDayAvgPricesJson.get(name) == null
- ? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(oneDayAvgPricesJson.get(name).getAsDouble(), count)));
- }
- if (!name.isEmpty() && (type == SkyblockerConfig.Average.THREE_DAY || type == SkyblockerConfig.Average.BOTH)) {
- lines.add(Text.literal(String.format("%-19s", "3 Day Avg. Price:"))
- .formatted(Formatting.GOLD)
- .append(threeDayAvgPricesJson.get(name) == null
- ? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(threeDayAvgPricesJson.get(name).getAsDouble(), count)));
+ // "No data" line because of API not keeping old data, it causes NullPointerException
+ if (type == SkyblockerConfig.Average.ONE_DAY || type == SkyblockerConfig.Average.BOTH) {
+ lines.add(
+ Text.literal(String.format("%-19s", "1 Day Avg. Price:"))
+ .formatted(Formatting.GOLD)
+ .append(oneDayAvgPricesJson.get(name) == null
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : getCoinsMessage(oneDayAvgPricesJson.get(name).getAsDouble(), count)
+ )
+ );
+ }
+ if (type == SkyblockerConfig.Average.THREE_DAY || type == SkyblockerConfig.Average.BOTH) {
+ lines.add(
+ Text.literal(String.format("%-19s", "3 Day Avg. Price:"))
+ .formatted(Formatting.GOLD)
+ .append(threeDayAvgPricesJson.get(name) == null
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : getCoinsMessage(threeDayAvgPricesJson.get(name).getAsDouble(), count)
+ )
+ );
+ }
}
}
}
if (SkyblockerConfig.get().general.itemTooltip.enableMuseumDate && !bazaarOpened) {
if (isMuseumJson == null) {
- if (!nullMsgSend) {
- client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
- nullMsgSend = true;
+ nullWarning();
+ }
+ else {
+ String timestamp = getTimestamp(stack);
+
+ if (isMuseumJson.has(name)) {
+ String itemCategory = isMuseumJson.get(name).toString().replaceAll("\"", "");
+ String format = switch (itemCategory) {
+ case "Weapons" -> "%-18s";
+ case "Armor" -> "%-19s";
+ default -> "%-20s";
+ };
+ lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")"))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(Text.literal(timestamp).formatted(Formatting.RED)));
+ } else if (!timestamp.isEmpty()) {
+ lines.add(Text.literal(String.format("%-21s", "Obtained: "))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(Text.literal(timestamp).formatted(Formatting.RED)));
}
- } else if (isMuseumJson.has(name)) {
- String itemCategory = isMuseumJson.get(name).toString().replaceAll("\"", "");
- String format = switch (itemCategory) {
- case "Weapons" -> "%-18s";
- case "Armor" -> "%-19s";
- default -> "%-20s";
- };
- lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")"))
- .formatted(Formatting.LIGHT_PURPLE)
- .append(Text.literal(timestamp != null ? timestamp : "").formatted(Formatting.RED)));
- } else if (timestamp != null) {
- lines.add(Text.literal(String.format("%-21s", "Obtained: "))
- .formatted(Formatting.LIGHT_PURPLE)
- .append(Text.literal(timestamp).formatted(Formatting.RED)));
}
}
}
+
+ private static void nullWarning() {
+ if (!nullMsgSend && client.player != null) {
+ client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
+ nullMsgSend = true;
+ }
+ }
- public static NbtCompound getInternalNameForItem(ItemStack stack) {
+ public static NbtCompound getItemNBT(ItemStack stack) {
if (stack == null) return null;
return stack.getNbt();
}
/**
* this method converts the "timestamp" variable into the same date format as Hypixel represents it in the museum.
- * Currently there are two types of timestamps the legacy which is built like this
+ * Currently, there are two types of timestamps the legacy which is built like this
* "dd/MM/yy hh:mm" ("25/04/20 16:38") and the current which is built like this
* "MM/dd/yy hh:mm aa" ("12/24/20 11:08 PM"). Since Hypixel transforms the two formats into one format without
* taking into account of their formats, we do the same. The final result looks like this
@@ -192,83 +216,106 @@ public class PriceInfoTooltip {
* This causes the museum rank to be much worse than it should be.
*
* @param stack the item under the pointer
- * @return if the item have an "Timestamp" it will be shown formated on the tooltip
+ * @return if the item have a "Timestamp" it will be shown formated on the tooltip
*/
public static String getTimestamp(ItemStack stack) {
- NbtCompound tag = getInternalNameForItem(stack);
- String internalName = null;
+ NbtCompound tag = getItemNBT(stack);
+
if (tag != null && tag.contains("ExtraAttributes", 10)) {
NbtCompound ea = tag.getCompound("ExtraAttributes");
if (ea.contains("timestamp", 8)) {
- internalName = ea.getString("timestamp");
- SimpleDateFormat dt = new SimpleDateFormat("MM/dd/yy");
+ SimpleDateFormat nbtFormat = new SimpleDateFormat("MM/dd/yy");
try {
- Date date = dt.parse(internalName);
- SimpleDateFormat dt1 = new SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH);
- internalName = dt1.format(date);
+ Date date = nbtFormat.parse(ea.getString("timestamp"));
+ SimpleDateFormat skyblockerFormat = new SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH);
+ return skyblockerFormat.format(date);
} catch (ParseException e) {
LOGGER.warn("[Skyblocker-tooltip] getTimestamp", e);
}
}
}
- return internalName;
+
+ return "";
}
public static String getInternalNameFromNBT(ItemStack stack) {
- NbtCompound tag = getInternalNameForItem(stack);
- String internalName = null;
+ NbtCompound tag = getItemNBT(stack);
if (tag != null && tag.contains("ExtraAttributes", 10)) {
NbtCompound ea = tag.getCompound("ExtraAttributes");
if (ea.contains("id", 8)) {
- internalName = ea.getString("id");
- } else {
- return null;
- }
+ String internalName = ea.getString("id");
- if ("ENCHANTED_BOOK".equals(internalName)) {
- if (ea.contains("enchantments")) {
- NbtCompound enchants = ea.getCompound("enchantments");
- String enchant = enchants.getKeys().stream().findFirst().get();
- internalName += "-" + enchant.toUpperCase(Locale.ENGLISH) + "-" + enchants.getInt(enchant);
- }
- } else if ("PET".equals(internalName)) {
- if (ea.contains("petInfo")) {
- JsonObject petInfo = gson.fromJson(ea.getString("petInfo"), JsonObject.class);
- internalName += "-" + petInfo.get("type").getAsString() + "-" + petInfo.get("tier").getAsString();
- }
- } else if ("POTION".equals(internalName)) {
- String enhanced = ea.contains("enhanced") ? "-ENHANCED" : "";
- String extended = ea.contains("extended") ? "-EXTENDED" : "";
- String splash = ea.contains("splash") ? "-SPLASH" : "";
- if (ea.contains("potion") && ea.contains("potion_level")) {
- internalName += "-" + ea.getString("potion").toUpperCase(Locale.ENGLISH) + "-" + ea.getInt("potion_level")
- + enhanced + extended + splash;
- }
- } else if ("RUNE".equals(internalName)) {
- if (ea.contains("runes")) {
- NbtCompound runes = ea.getCompound("runes");
- String rune = runes.getKeys().stream().findFirst().get();
- internalName += "-" + rune.toUpperCase(Locale.ENGLISH) + "-" + runes.getInt(rune);
+ // Transformation to API format.
+ if ("ENCHANTED_BOOK".equals(internalName)) {
+ if (ea.contains("enchantments")) {
+ NbtCompound enchants = ea.getCompound("enchantments");
+ String enchant = enchants.getKeys().stream().findFirst().get();
+ return internalName + "-" + enchant.toUpperCase(Locale.ENGLISH) + "-" + enchants.getInt(enchant);
+ }
+ } else if ("PET".equals(internalName)) {
+ if (ea.contains("petInfo")) {
+ JsonObject petInfo = gson.fromJson(ea.getString("petInfo"), JsonObject.class);
+ return internalName + "-" + petInfo.get("type").getAsString() + "-" + petInfo.get("tier").getAsString();
+ }
+ } else if ("POTION".equals(internalName)) {
+ // New API just contains 'enhanced' tag.
+ String enhanced = ea.contains("enhanced") ? "-ENHANCED" : "";
+ //String extended = ea.contains("extended") ? "-EXTENDED" : "";
+ //String splash = ea.contains("splash") ? "-SPLASH" : "";
+ if (ea.contains("potion") && ea.contains("potion_level")) {
+ return internalName + "-" + ea.getString("potion").toUpperCase(Locale.ENGLISH) + "-" + ea.getInt("potion_level")
+ + enhanced; //+ extended + splash;
+ }
+ } else if ("RUNE".equals(internalName)) {
+ if (ea.contains("runes")) {
+ NbtCompound runes = ea.getCompound("runes");
+ String rune = runes.getKeys().stream().findFirst().get();
+ return internalName + "-" + rune.toUpperCase(Locale.ENGLISH) + "-" + runes.getInt(rune);
+ }
}
- }
+ return internalName;
+ }
+ else
+ return null;
}
- return internalName;
+ else
+ return null;
}
private static Text getCoinsMessage(double price, int count) {
if (count == 1) {
String priceString = String.format(Locale.ENGLISH, "%1$,.1f", price);
return Text.literal(priceString + " Coins").formatted(Formatting.DARK_AQUA);
- } else {
- String priceString = String.format(Locale.ENGLISH, "%1$,.1f", price * count);
- MutableText priceText = Text.literal(priceString + " Coins ").formatted(Formatting.DARK_AQUA);
- priceString = String.format(Locale.ENGLISH, "%1$,.1f", price);
- MutableText priceText2 = Text.literal( "(" + priceString + " each)").formatted(Formatting.GRAY);
- return priceText.append(priceText2);
+ }
+ else {
+ String priceStringTotal = String.format(Locale.ENGLISH, "%1$,.1f", price * count);
+ MutableText priceTextTotal = Text.literal(priceStringTotal + " Coins ").formatted(Formatting.DARK_AQUA);
+
+ String priceStringEach = String.format(Locale.ENGLISH, "%1$,.1f", price);
+ MutableText priceTextEach = Text.literal( "(" + priceStringEach + " each)").formatted(Formatting.GRAY);
+
+ return priceTextTotal.append(priceTextEach);
+ }
+ }
+
+ private static Text getMotesMessage(int price, int count) {
+ float motesMultiplier = SkyblockerConfig.get().locations.rift.mcGrubberStacks * 0.05f + 1;
+ if (count == 1) {
+ String priceString = String.format(Locale.ENGLISH, "%1$,.1f", price * motesMultiplier).replace(".0", "");
+ return Text.literal(priceString + " Motes").formatted(Formatting.DARK_AQUA);
+ }
+ else {
+ String priceStringTotal = String.format(Locale.ENGLISH, "%1$,.1f", price * count * motesMultiplier).replace(".0", "");
+ MutableText priceTextTotal = Text.literal(priceStringTotal + " Motes ").formatted(Formatting.DARK_AQUA);
+
+ String priceStringEach = String.format(Locale.ENGLISH, "%1$,.1f", price * motesMultiplier).replace(".0", "");
+ MutableText priceTextEach = Text.literal( "(" + priceStringEach + " each)").formatted(Formatting.GRAY);
+
+ return priceTextTotal.append(priceTextEach);
}
}
@@ -277,7 +324,7 @@ public class PriceInfoTooltip {
public static int minute = -1;
public static void init() {
skyblocker.scheduler.scheduleCyclic(() -> {
- if (!Utils.isOnSkyblock && 0 < minute++) {
+ if (!Utils.isOnSkyblock() && 0 < minute++) {
nullMsgSend = false;
return;
}
@@ -287,108 +334,58 @@ public class PriceInfoTooltip {
SkyblockerConfig.Average type = SkyblockerConfig.get().general.itemTooltip.avg;
if (type == SkyblockerConfig.Average.BOTH || oneDayAvgPricesJson == null || threeDayAvgPricesJson == null) {
- futureList.add(CompletableFuture.runAsync(() -> downloadAvgPrices(SkyblockerConfig.Average.THREE_DAY)));
- futureList.add(CompletableFuture.runAsync(() -> downloadAvgPrices(SkyblockerConfig.Average.ONE_DAY)));
- } else {
- futureList.add(CompletableFuture.runAsync(() -> downloadAvgPrices(type)));
+ futureList.add(CompletableFuture.runAsync(() -> oneDayAvgPricesJson = downloadPrices("1 day avg")));
+ futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg")));
+ }
+ else if (type == SkyblockerConfig.Average.ONE_DAY) {
+ futureList.add(CompletableFuture.runAsync(() -> oneDayAvgPricesJson = downloadPrices("1 day avg")));
+ }
+ else if (type == SkyblockerConfig.Average.THREE_DAY) {
+ futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg")));
}
}
- if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN) {
- futureList.add(CompletableFuture.runAsync(PriceInfoTooltip::downloadLowestPrices));
- }
- if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice) {
- futureList.add(CompletableFuture.runAsync(PriceInfoTooltip::downloadBazaarPrices));
- }
- if (SkyblockerConfig.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null) {
- futureList.add(CompletableFuture.runAsync(PriceInfoTooltip::downloadNPCPrices));
- }
- if (SkyblockerConfig.get().general.itemTooltip.enableMuseumDate && isMuseumJson == null) {
- futureList.add(CompletableFuture.runAsync(PriceInfoTooltip::downloadIsMuseum));
- }
+ if (SkyblockerConfig.get().general.itemTooltip.enableLowestBIN)
+ futureList.add(CompletableFuture.runAsync(() -> lowestPricesJson = downloadPrices("lowest bins")));
+
+ if (SkyblockerConfig.get().general.itemTooltip.enableBazaarPrice)
+ futureList.add(CompletableFuture.runAsync(() -> bazaarPricesJson = downloadPrices("bazaar")));
+
+ if (SkyblockerConfig.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null)
+ futureList.add(CompletableFuture.runAsync(() -> npcPricesJson = downloadPrices("npc")));
+
+ if (SkyblockerConfig.get().general.itemTooltip.enableMuseumDate && isMuseumJson == null)
+ futureList.add(CompletableFuture.runAsync(() -> isMuseumJson = downloadPrices("museum")));
+
+ if (SkyblockerConfig.get().general.itemTooltip.enableMotesPrice && motesPricesJson == null)
+ futureList.add(CompletableFuture.runAsync(() -> motesPricesJson = downloadPrices("motes")));
+
minute++;
CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]))
.whenComplete((unused, throwable) -> nullMsgSend = false);
}, 1200);
}
- private static void downloadAvgPrices(SkyblockerConfig.Average type) {
- JsonObject result = null;
- String avgDay = null;
- switch (type) {
- case ONE_DAY -> avgDay = "1day.json.gz";
- case THREE_DAY -> avgDay = "3day.json.gz";
- }
+ private static JsonObject downloadPrices(String type) {
try {
- URL apiAddr = new URL("https://moulberry.codes/auction_averages_lbin/" + avgDay);
- try (InputStream src = apiAddr.openStream()) {
- try (GZIPInputStream gzipOutput = new GZIPInputStream(src)) {
- try (InputStreamReader reader = new InputStreamReader(gzipOutput)) {
- result = new Gson().fromJson(reader, JsonObject.class);
- }
- }
- }
+ String url = apiAddresses.get(type);
+ URL apiAddress = new URL(url);
+ InputStream src = apiAddress.openStream();
+ InputStreamReader reader = new InputStreamReader(url.contains(".gz") ? new GZIPInputStream(src) : src);
+ return new Gson().fromJson(reader, JsonObject.class);
} catch (IOException e) {
- LOGGER.warn("[Skyblocker] Failed to download average BIN prices!", e);
- }
- switch (type) {
- case ONE_DAY -> oneDayAvgPricesJson = result;
- case THREE_DAY -> threeDayAvgPricesJson = result;
+ LOGGER.warn("[Skyblocker] Failed to download " + type + " prices!", e);
+ return null;
}
}
- private static void downloadBazaarPrices() {
- JsonObject result = null;
- try {
- URL apiAddr = new URL("https://hysky.de/api/bazaar");
- InputStreamReader reader = new InputStreamReader(apiAddr.openStream());
- result = new Gson().fromJson(reader, JsonObject.class);
- } catch (IOException e) {
- LOGGER.warn("[Skyblocker] Failed to download bazaar prices!", e);
- }
- bazaarPricesJson = result;
- }
-
- private static void downloadLowestPrices() {
- JsonObject result = null;
- try {
- URL apiAddr = new URL("https://lb.tricked.pro/lowestbins");
- InputStreamReader reader = new InputStreamReader(apiAddr.openStream());
- result = new Gson().fromJson(reader, JsonObject.class);
- } catch (IOException e) {
- LOGGER.warn("[Skyblocker] Failed to download lowest BIN prices from the main source!", e);
- try {
- URL apiAddr = new URL("https://lb2.tricked.pro/lowestbins");
- InputStreamReader reader = new InputStreamReader(apiAddr.openStream());
- result = new Gson().fromJson(reader, JsonObject.class);
- } catch (IOException e2) {
- LOGGER.warn("[Skyblocker] Failed to download lowest BIN prices from the backup source!", e2);
- }
- }
- lowestPricesJson = result;
+ static {
+ apiAddresses = new HashMap<>();
+ apiAddresses.put("1 day avg", "https://moulberry.codes/auction_averages_lbin/1day.json.gz");
+ apiAddresses.put("3 day avg", "https://moulberry.codes/auction_averages_lbin/3day.json.gz");
+ apiAddresses.put("bazaar", "https://hysky.de/api/bazaar");
+ apiAddresses.put("lowest bins", "https://lb.tricked.pro/lowestbins");
+ apiAddresses.put("npc", "https://hysky.de/api/npcprice");
+ apiAddresses.put("museum", "https://hysky.de/api/museum");
+ apiAddresses.put("motes", "https://hysky.de/api/motesprice");
}
-
- private static void downloadNPCPrices() {
- JsonObject result = null;
- try {
- URL apiAddr = new URL("https://hysky.de/api/npcprice");
- InputStreamReader reader = new InputStreamReader(apiAddr.openStream());
- result = new Gson().fromJson(reader, JsonObject.class);
- } catch (IOException e) {
- LOGGER.warn("[Skyblocker] Failed to download NPC prices!", e);
- }
- npcPricesJson = result;
- }
-
- private static void downloadIsMuseum() {
- JsonObject result = null;
- try {
- URL apiAddr = new URL("https://hysky.de/api/museum");
- InputStreamReader reader = new InputStreamReader(apiAddr.openStream());
- result = new Gson().fromJson(reader, JsonObject.class);
- } catch (IOException e) {
- LOGGER.warn("[Skyblocker] Failed to download museum items!", e);
- }
- isMuseumJson = result;
- }
-
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java
index 82c9fdf4..d05e5cb8 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java
@@ -1,9 +1,7 @@
package me.xmrvizzy.skyblocker.skyblock.item;
import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
import me.xmrvizzy.skyblocker.utils.Utils;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.MinecraftClient;
@@ -16,11 +14,7 @@ import net.minecraft.text.Text;
import net.minecraft.util.Util;
import org.lwjgl.glfw.GLFW;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.net.URLConnection;
+import java.util.concurrent.CompletableFuture;
public class WikiLookup {
public static KeyBinding wikiLookup;
@@ -48,26 +42,15 @@ public class WikiLookup {
}
public static void openWiki(Slot slot) {
- if (Utils.isOnSkyblock) {
+ if (Utils.isOnSkyblock()) {
id = getSkyblockId(slot);
try {
- //Setting up a connection with the repo
- String urlString = "https://raw.githubusercontent.com/NotEnoughUpdates/NotEnoughUpdates-REPO/master/items/" + id + ".json";
- URL url = new URL(urlString);
- URLConnection request = url.openConnection();
- request.connect();
-
- //yoinking the wiki link
- JsonElement root = JsonParser.parseReader(new InputStreamReader((InputStream) request.getContent()));
- JsonObject rootobj = root.getAsJsonObject();
- String wikiLink = rootobj.get("info").getAsJsonArray().get(1).getAsString();
- Util.getOperatingSystem().open(wikiLink);
- } catch (IOException | NullPointerException e) {
- e.printStackTrace();
- client.player.sendMessage(Text.of("Can't locate a wiki article for this item..."), false);
+ String wikiLink = ItemRegistry.getWikiLink(id);
+ CompletableFuture.runAsync(() -> Util.getOperatingSystem().open(wikiLink));
} catch (IndexOutOfBoundsException | IllegalStateException e) {
e.printStackTrace();
- client.player.sendMessage(Text.of("Error while retrieving wiki article..."), false);
+ if (client.player != null)
+ client.player.sendMessage(Text.of("Error while retrieving wiki article..."), false);
}
}
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java
index 99a06337..3a1f91d3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java
@@ -1,17 +1,15 @@
package me.xmrvizzy.skyblocker.skyblock.itemlist;
import com.mojang.blaze3d.systems.RenderSystem;
-
import me.xmrvizzy.skyblocker.mixin.RecipeBookWidgetAccessor;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.Drawable;
-import net.minecraft.client.gui.DrawableHelper;
import net.minecraft.client.gui.Selectable;
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
-import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.screen.AbstractRecipeScreenHandler;
import net.minecraft.text.Text;
@@ -40,51 +38,65 @@ public class ItemListWidget extends RecipeBookWidget implements Drawable, Select
this.searchField = ((RecipeBookWidgetAccessor)this).getSearchField();
int x = (this.parentWidth - 147) / 2 - this.leftOffset;
int y = (this.parentHeight - 166) / 2;
- this.results = new SearchResultsWidget(this.client, x , y);
- this.updateSearchResult();
+ if (ItemRegistry.filesImported) {
+ this.results = new SearchResultsWidget(this.client, x, y);
+ this.updateSearchResult();
+ }
}
@Override
- public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
if (this.isOpen()) {
- matrices.push();
+ MatrixStack matrices = context.getMatrices();
+ matrices.push();
matrices.translate(0.0D, 0.0D, 100.0D);
- RenderSystem.setShader(GameRenderer::getPositionTexProgram);
- RenderSystem.setShaderTexture(0, TEXTURE);
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
this.searchField = ((RecipeBookWidgetAccessor)this).getSearchField();
int i = (this.parentWidth - 147) / 2 - this.leftOffset;
int j = (this.parentHeight - 166) / 2;
- DrawableHelper.drawTexture(matrices, i, j, 1, 1, 147, 166);
+ context.drawTexture(TEXTURE, i, j, 1, 1, 147, 166);
this.searchField = ((RecipeBookWidgetAccessor)this).getSearchField();
- if (!this.searchField.isFocused() && this.searchField.getText().isEmpty()) {
+
+ if (!ItemRegistry.filesImported && !this.searchField.isFocused() && this.searchField.getText().isEmpty()) {
+ Text hintText = (Text.literal("Loading...")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY);
+ context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1);
+ } else if (!this.searchField.isFocused() && this.searchField.getText().isEmpty()) {
Text hintText = (Text.translatable("gui.recipebook.search_hint")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY);
- drawTextWithShadow(matrices, this.client.textRenderer, hintText, i + 25, j + 14, -1);
+ context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1);
} else {
- this.searchField.render(matrices, mouseX, mouseY, delta);
+ this.searchField.render(context, mouseX, mouseY, delta);
+ }
+ if (ItemRegistry.filesImported){
+ if (results == null) {
+ int x = (this.parentWidth - 147) / 2 - this.leftOffset;
+ int y = (this.parentHeight - 166) / 2;
+ this.results = new SearchResultsWidget(this.client, x, y);
+ }
+ this.updateSearchResult();
+ this.results.render(context, mouseX, mouseY, delta);
}
- this.updateSearchResult();
- this.results.render(matrices, mouseX, mouseY, delta);
matrices.pop();
}
}
@Override
- public void drawTooltip(MatrixStack matrices, int x, int y, int mouseX, int mouseY) {
- if (this.isOpen()) {
- this.results.drawTooltip(matrices, mouseX, mouseY);
+ public void drawTooltip(DrawContext context, int x, int y, int mouseX, int mouseY) {
+ if (this.isOpen() && ItemRegistry.filesImported && results != null) {
+ this.results.drawTooltip(context, mouseX, mouseY);
}
}
@Override
- public boolean mouseClicked(double mouseX, double mouseY, int button) {
- if (!this.isOpen() || this.client.player.isSpectator()) {
- return false;
- }
- if (this.searchField != null && this.searchField.mouseClicked(mouseX, mouseY, button)) {
- this.results.closeRecipeView();
- return true;
- }
- return this.results.mouseClicked(mouseX, mouseY, button);
- }
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (this.isOpen() && !this.client.player.isSpectator() && ItemRegistry.filesImported && results != null) {
+ if (this.searchField != null && this.searchField.mouseClicked(mouseX, mouseY, button)) {
+ this.results.closeRecipeView();
+ this.searchField.setFocused(true);
+ return true;
+ } else
+ this.searchField.setFocused(false);
+ return this.results.mouseClicked(mouseX, mouseY, button);
+ } else
+ return false;
+ }
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java
index 008ff191..dc63e351 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java
@@ -2,55 +2,33 @@ package me.xmrvizzy.skyblocker.skyblock.itemlist;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
-
-import net.fabricmc.loader.api.FabricLoader;
+import me.xmrvizzy.skyblocker.utils.NEURepo;
+import net.minecraft.client.MinecraftClient;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
-import org.eclipse.jgit.api.Git;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import net.minecraft.text.Text;
import java.io.File;
+import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public class ItemRegistry {
- protected static final String REMOTE_ITEM_REPO = "https://github.com/KonaeAkira/NotEnoughUpdates-REPO.git";
- protected static final Path LOCAL_ITEM_REPO_DIR = FabricLoader.getInstance().getConfigDir().resolve("skyblocker/item-repo");
-
- private static final Path ITEM_LIST_DIR = LOCAL_ITEM_REPO_DIR.resolve("items");
+ protected static final Path ITEM_LIST_DIR = NEURepo.LOCAL_REPO_DIR.resolve("items");
protected static final List<ItemStack> items = new ArrayList<>();
protected static final Map<String, ItemStack> itemsMap = new HashMap<>();
- protected static final List<Recipe> recipes = new ArrayList<>();
+ protected static final List<SkyblockCraftingRecipe> recipes = new ArrayList<>();
+ public static final MinecraftClient client = MinecraftClient.getInstance();
+ public static boolean filesImported = false;
- // TODO: make async
public static void init() {
- updateItemRepo();
- ItemStackBuilder.init();
- importItemFiles();
- }
-
- private static void updateItemRepo() {
- if (!Files.isDirectory(LOCAL_ITEM_REPO_DIR)) {
- try {
- Git.cloneRepository()
- .setURI(REMOTE_ITEM_REPO)
- .setDirectory(LOCAL_ITEM_REPO_DIR.toFile())
- .setBranchesToClone(List.of("refs/heads/master"))
- .setBranch("refs/heads/master")
- .call();
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- try {
- Git.open(LOCAL_ITEM_REPO_DIR.toFile()).pull().call();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+ NEURepo.runAsyncAfterLoad(ItemStackBuilder::loadPetNums);
+ NEURepo.runAsyncAfterLoad(ItemRegistry::importItemFiles);
}
private static void importItemFiles() {
@@ -58,7 +36,9 @@ public class ItemRegistry {
File dir = ITEM_LIST_DIR.toFile();
File[] files = dir.listFiles();
- assert files != null;
+ if (files == null) {
+ return;
+ }
for (File file : files) {
Path path = ITEM_LIST_DIR.resolve(file.getName());
try {
@@ -77,74 +57,67 @@ public class ItemRegistry {
}
for (JsonObject jsonObj : jsonObjs)
if (jsonObj.has("recipe")) {
- recipes.add(Recipe.fromJsonObject(jsonObj));
+ recipes.add(SkyblockCraftingRecipe.fromJsonObject(jsonObj));
}
items.sort((lhs, rhs) -> {
- String lhsInternalName = lhs.getNbt().getCompound("ExtraAttributes").getString("id");
+ String lhsInternalName = getInternalName(lhs);
String lhsFamilyName = lhsInternalName.replaceAll(".\\d+$", "");
- String rhsInternalName = rhs.getNbt().getCompound("ExtraAttributes").getString("id");
+ String rhsInternalName = getInternalName(rhs);
String rhsFamilyName = rhsInternalName.replaceAll(".\\d+$", "");
if (lhsFamilyName.equals(rhsFamilyName)) {
if (lhsInternalName.length() != rhsInternalName.length())
return lhsInternalName.length() - rhsInternalName.length();
- else
- return lhsInternalName.compareTo(rhsInternalName);
+ else return lhsInternalName.compareTo(rhsInternalName);
}
return lhsFamilyName.compareTo(rhsFamilyName);
});
+ filesImported = true;
}
- public static List<Recipe> getRecipes(String internalName) {
- List<Recipe> result = new ArrayList<>();
- for (Recipe recipe : recipes)
- if (recipe.result.getNbt().getCompound("ExtraAttributes").getString("id").equals(internalName))
- result.add(recipe);
- for (Recipe recipe : recipes)
+ public static String getWikiLink(String internalName) {
+ try {
+ String fileContent = Files.readString(ITEM_LIST_DIR.resolve(internalName + ".json"));
+ JsonObject fileJson = JsonParser.parseString(fileContent).getAsJsonObject();
+ //TODO optional official or unofficial wiki link
+ try {
+ return fileJson.get("info").getAsJsonArray().get(1).getAsString();
+ } catch (IndexOutOfBoundsException e) {
+ return fileJson.get("info").getAsJsonArray().get(0).getAsString();
+ }
+ } catch (IOException | NullPointerException e) {
+ e.printStackTrace();
+ client.player.sendMessage(Text.of("Can't locate a wiki article for this item..."), false);
+ return null;
+ }
+ }
+
+ public static List<SkyblockCraftingRecipe> getRecipes(String internalName) {
+ List<SkyblockCraftingRecipe> result = new ArrayList<>();
+ for (SkyblockCraftingRecipe recipe : recipes)
+ if (getInternalName(recipe.result).equals(internalName)) result.add(recipe);
+ for (SkyblockCraftingRecipe recipe : recipes)
for (ItemStack ingredient : recipe.grid)
- if (!ingredient.getItem().equals(Items.AIR) && ingredient.getNbt().getCompound("ExtraAttributes").getString("id").equals(internalName)) {
+ if (!ingredient.getItem().equals(Items.AIR) && getInternalName(ingredient).equals(internalName)) {
result.add(recipe);
break;
}
return result;
}
-}
-class Recipe {
- private static final Logger LOGGER = LoggerFactory.getLogger(Recipe.class);
- String text = "";
- final List<ItemStack> grid = new ArrayList<>(9);
- ItemStack result;
-
- public static Recipe fromJsonObject(JsonObject jsonObj) {
- Recipe recipe = new Recipe();
- if (jsonObj.has("crafttext")) recipe.text = jsonObj.get("crafttext").getAsString();
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A1").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A2").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A3").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B1").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B2").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B3").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C1").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C2").getAsString()));
- recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C3").getAsString()));
- recipe.result = ItemRegistry.itemsMap.get(jsonObj.get("internalname").getAsString());
- return recipe;
+ public static List<SkyblockCraftingRecipe> getRecipes() {
+ return recipes;
}
- private static ItemStack getItemStack(String internalName) {
- try {
- if (internalName.length() > 0) {
- int count = Integer.parseInt(internalName.split(":")[1]);
- internalName = internalName.split(":")[0];
- ItemStack itemStack = ItemRegistry.itemsMap.get(internalName).copy();
- itemStack.setCount(count);
- return itemStack;
- }
- }
- catch(Exception e) {
- LOGGER.error("[Skyblocker-Recipe] "+internalName,e);
- }
- return Items.AIR.getDefaultStack();
+ /**
+ * Get Internal name of an ItemStack
+ *
+ * @param itemStack ItemStack to get internal name from
+ * @return internal name of the given ItemStack
+ */
+ public static String getInternalName(ItemStack itemStack) {
+ if (itemStack.getNbt() == null) return "";
+ return itemStack.getNbt().getCompound("ExtraAttributes").getString("id");
}
-} \ No newline at end of file
+}
+
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java
index b2d909a8..d420d54f 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java
@@ -4,6 +4,7 @@ import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import me.xmrvizzy.skyblocker.utils.NEURepo;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.*;
import net.minecraft.text.Text;
@@ -16,10 +17,10 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ItemStackBuilder {
- private final static Path PETNUMS_PATH = ItemRegistry.LOCAL_ITEM_REPO_DIR.resolve("constants/petnums.json");
+ private final static Path PETNUMS_PATH = NEURepo.LOCAL_REPO_DIR.resolve("constants/petnums.json");
private static JsonObject petNums;
- public static void init() {
+ public static void loadPetNums() {
try {
petNums = JsonParser.parseString(Files.readString(PETNUMS_PATH)).getAsJsonObject();
} catch (Exception e) {
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java
index 41e5469d..12636ce1 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java
@@ -1,17 +1,22 @@
package me.xmrvizzy.skyblocker.skyblock.itemlist;
import java.util.List;
+import java.util.function.Function;
+import java.util.ArrayList;
+import com.google.common.collect.Lists;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.gui.widget.ClickableWidget;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
+import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
@@ -36,23 +41,26 @@ public class ResultButtonWidget extends ClickableWidget {
}
@Override
- public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) {
+ public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) {
MinecraftClient client = MinecraftClient.getInstance();
- RenderSystem.setShader(GameRenderer::getPositionTexProgram);
- RenderSystem.setShaderTexture(0, BACKGROUND_TEXTURE);
// this.drawTexture(matrices, this.x, this.y, 29, 206, this.width, this.height);
- DrawableHelper.drawTexture(matrices, this.getX(), this.getY(), 29, 206, this.getWidth(), this.getHeight());
+ context.drawTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), 29, 206, this.getWidth(), this.getHeight());
// client.getItemRenderer().renderInGui(this.itemStack, this.x + 4, this.y + 4);
- client.getItemRenderer().renderInGui(matrices, this.itemStack, this.getX() + 4, this.getY() + 4);
+ context.drawItem(this.itemStack, this.getX() + 4, this.getY() + 4);
// client.getItemRenderer().renderGuiItemOverlay(client.textRenderer, itemStack, this.x + 4, this.y + 4);
- client.getItemRenderer().renderGuiItemOverlay(matrices, client.textRenderer, itemStack, this.getX() + 4, this.getY() + 4);
+ context.drawItemInSlot(client.textRenderer, itemStack, this.getX() + 4, this.getY() + 4);
}
- public void renderTooltip(MatrixStack matrices, int mouseX, int mouseY) {
+ public void renderTooltip(DrawContext context, int mouseX, int mouseY) {
MinecraftClient client = MinecraftClient.getInstance();
- List<Text> tooltip = client.currentScreen.getTooltipFromItem(this.itemStack);
- // TODO : add null check with log error
- client.currentScreen.renderTooltip(matrices, tooltip, mouseX, mouseY);
+ List<Text> tooltip = Screen.getTooltipFromItem(client, this.itemStack);
+ List<OrderedText> orderedTooltip = new ArrayList<>();
+
+ for(int i = 0; i < tooltip.size(); i++) {
+ orderedTooltip.add(tooltip.get(i).asOrderedText());
+ }
+
+ client.currentScreen.setTooltip(orderedTooltip);
}
@Override
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java
index 64ba237d..058495a2 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java
@@ -2,6 +2,8 @@ package me.xmrvizzy.skyblocker.skyblock.itemlist;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.widget.ToggleButtonWidget;
import net.minecraft.client.util.math.MatrixStack;
@@ -22,7 +24,7 @@ public class SearchResultsWidget implements Drawable {
private final int parentY;
private final List<ItemStack> searchResults = new ArrayList<>();
- private List<Recipe> recipeResults = new ArrayList<>();
+ private List<SkyblockCraftingRecipe> recipeResults = new ArrayList<>();
private String searchText = null;
private final List<ResultButtonWidget> resultButtons = new ArrayList<>();
private final ToggleButtonWidget nextPageButton;
@@ -79,7 +81,7 @@ public class SearchResultsWidget implements Drawable {
private void updateButtons() {
if (this.displayRecipes) {
- Recipe recipe = this.recipeResults.get(this.currentPage);
+ SkyblockCraftingRecipe recipe = this.recipeResults.get(this.currentPage);
for (ResultButtonWidget button : resultButtons)
button.clearItemStack();
resultButtons.get(5).setItemStack(recipe.grid.get(0));
@@ -106,32 +108,33 @@ public class SearchResultsWidget implements Drawable {
this.nextPageButton.active = this.currentPage < this.pageCount - 1;
}
- public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
RenderSystem.disableDepthTest();
if (this.displayRecipes) {
- String craftText = this.recipeResults.get(this.currentPage).text;
- this.client.textRenderer.drawWithShadow(matrices, craftText, this.parentX + 11, this.parentY + 31, 0xffffffff);
+ String craftText = this.recipeResults.get(this.currentPage).craftText;
+ context.drawTextWithShadow(textRenderer, craftText, this.parentX + 11, this.parentY + 31, 0xffffffff);
Text resultText = this.recipeResults.get(this.currentPage).result.getName();
- this.client.textRenderer.drawWithShadow(matrices, resultText, this.parentX + 11, this.parentY + 43, 0xffffffff);
- this.client.textRenderer.drawWithShadow(matrices, "▶", this.parentX + 96, this.parentY + 90, 0xaaffffff);
+ context.drawTextWithShadow(textRenderer, resultText, this.parentX + 11, this.parentY + 43, 0xffffffff);
+ context.drawTextWithShadow(textRenderer, "▶", this.parentX + 96, this.parentY + 90, 0xaaffffff);
}
for (ResultButtonWidget button : resultButtons)
- button.render(matrices, mouseX, mouseY, delta);
+ button.render(context, mouseX, mouseY, delta);
if (this.pageCount > 1) {
String string = (this.currentPage + 1) + "/" + this.pageCount;
int dx = this.client.textRenderer.getWidth(string) / 2;
- this.client.textRenderer.draw(matrices, string, this.parentX - dx + 73, this.parentY + 141, -1);
+ context.drawText(textRenderer, string, this.parentX - dx + 73, this.parentY + 141, -1, false);
}
- if (this.prevPageButton.active) this.prevPageButton.render(matrices, mouseX, mouseY, delta);
- if (this.nextPageButton.active) this.nextPageButton.render(matrices, mouseX, mouseY, delta);
+ if (this.prevPageButton.active) this.prevPageButton.render(context, mouseX, mouseY, delta);
+ if (this.nextPageButton.active) this.nextPageButton.render(context, mouseX, mouseY, delta);
RenderSystem.enableDepthTest();
}
- public void drawTooltip(MatrixStack matrices, int mouseX, int mouseY) {
+ public void drawTooltip(DrawContext context, int mouseX, int mouseY) {
RenderSystem.disableDepthTest();
for (ResultButtonWidget button : resultButtons)
if (button.isMouseOver(mouseX, mouseY))
- button.renderTooltip(matrices, mouseX, mouseY);
+ button.renderTooltip(context, mouseX, mouseY);
RenderSystem.enableDepthTest();
}
@@ -142,7 +145,7 @@ public class SearchResultsWidget implements Drawable {
continue;
}
String internalName = button.itemStack.getNbt().getCompound("ExtraAttributes").getString("id");
- List<Recipe> recipes = ItemRegistry.getRecipes(internalName);
+ List<SkyblockCraftingRecipe> recipes = ItemRegistry.getRecipes(internalName);
if (!recipes.isEmpty()) {
this.recipeResults = recipes;
this.currentPage = 0;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java
new file mode 100644
index 00000000..29aed7a1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.itemlist;
+
+import com.google.gson.JsonObject;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SkyblockCraftingRecipe {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SkyblockCraftingRecipe.class);
+ String craftText = "";
+ final List<ItemStack> grid = new ArrayList<>(9);
+ ItemStack result;
+
+ public static SkyblockCraftingRecipe fromJsonObject(JsonObject jsonObj) {
+ SkyblockCraftingRecipe recipe = new SkyblockCraftingRecipe();
+ if (jsonObj.has("crafttext")) recipe.craftText = jsonObj.get("crafttext").getAsString();
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A1").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A2").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A3").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B1").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B2").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B3").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C1").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C2").getAsString()));
+ recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C3").getAsString()));
+ recipe.result = ItemRegistry.itemsMap.get(jsonObj.get("internalname").getAsString());
+ return recipe;
+ }
+
+ private static ItemStack getItemStack(String internalName) {
+ try {
+ if (internalName.length() > 0) {
+ int count = internalName.split(":").length == 1 ? 1 : Integer.parseInt(internalName.split(":")[1]);
+ internalName = internalName.split(":")[0];
+ ItemStack itemStack = ItemRegistry.itemsMap.get(internalName).copy();
+ itemStack.setCount(count);
+ return itemStack;
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker-Recipe] " + internalName, e);
+ }
+ return Items.AIR.getDefaultStack();
+ }
+
+ public List<ItemStack> getGrid() {
+ return grid;
+ }
+
+ public ItemStack getResult() {
+ return result;
+ }
+
+ public String getCraftText() {
+ return craftText;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
index 99e1c529..1d58435e 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java
@@ -1,9 +1,11 @@
package me.xmrvizzy.skyblocker.skyblock.quicknav;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
-
import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.fabricmc.fabric.api.client.screen.v1.Screens;
+import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.StringNbtReader;
@@ -14,6 +16,17 @@ import java.util.Locale;
public class QuickNav {
private static final String skyblockHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}";
private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}";
+
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (Utils.isOnSkyblock() && SkyblockerConfig.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?>) {
+ String screenTitle = screen.getTitle().getString().trim();
+ List<QuickNavButton> buttons = QuickNav.init(screenTitle);
+ for (QuickNavButton button : buttons) Screens.getButtons(screen).add(button);
+ }
+ });
+ }
+
public static List<QuickNavButton> init(String screenTitle) {
List<QuickNavButton> buttons = new ArrayList<>();
SkyblockerConfig.QuickNav data = SkyblockerConfig.get().quickNav;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java
index e31827ab..c02463a3 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java
@@ -2,11 +2,12 @@ package me.xmrvizzy.skyblocker.skyblock.quicknav;
import com.mojang.blaze3d.systems.RenderSystem;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.mixin.HandledScreenAccessor;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.DrawableHelper;
+import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
@@ -57,47 +58,47 @@ public class QuickNavButton extends ClickableWidget {
public void onClick(double mouseX, double mouseY) {
if (!this.toggled) {
this.toggled = true;
- CLIENT.player.networkHandler.sendCommand(command.replace("/", ""));
+ SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown(command);
// TODO : add null check with log error
}
}
@Override
- public void renderButton(MatrixStack matrices, int mouseX, int mouseY, float delta) {
+ public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) {
this.updateCoordinates();
- RenderSystem.setShaderTexture(0, BUTTON_TEXTURE);
+ MatrixStack matrices = context.getMatrices();
RenderSystem.disableDepthTest();
// render button background
if (!this.toggled) {
if (this.index >= 6)
// this.drawTexture(matrices, this.x, this.y + 4, this.u, this.v + 4, this.width, this.height - 4);
- DrawableHelper.drawTexture(matrices, this.getX(), this.getY() + 4, this.u, this.v + 4, this.width, this.height - 4);
+ context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY() + 4, this.u, this.v + 4, this.width, this.height - 4);
else
// this.drawTexture(matrices, this.x, this.y, this.u, this.v, this.width, this.height - 4);
- DrawableHelper.drawTexture(matrices, this.getX(), this.getY() - 2, this.u, this.v, this.width, this.height - 4);
+ context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY() - 2, this.u, this.v, this.width, this.height - 4);
// } else this.drawTexture(matrices, this.x, this.y, this.u, this.v, this.width, this.height);
} else {
matrices.push();
//Move the top buttons 2 pixels up if they're selected
if (this.index < 6) matrices.translate(0f, -2f, 0f);
- DrawableHelper.drawTexture(matrices, this.getX(), this.getY(), this.u, this.v, this.width, this.height);
+ context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY(), this.u, this.v, this.width, this.height);
matrices.pop();
}
// render button icon
if (!this.toggled) {
if (this.index >= 6)
// CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 6);
- CLIENT.getItemRenderer().renderInGui(matrices, this.icon,this.getX() + 5, this.getY() + 6);
+ context.drawItem(this.icon,this.getX() + 5, this.getY() + 6);
else
// CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 9);
- CLIENT.getItemRenderer().renderInGui(matrices, this.icon,this.getX() + 5, this.getY() + 7);
+ context.drawItem(this.icon,this.getX() + 5, this.getY() + 7);
} else {
if (this.index >= 6)
// CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 9);
- CLIENT.getItemRenderer().renderInGui(matrices, this.icon,this.getX() + 5, this.getY() + 9);
+ context.drawItem(this.icon,this.getX() + 5, this.getY() + 9);
else
// CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 6);
- CLIENT.getItemRenderer().renderInGui(matrices, this.icon,this.getX() + 5, this.getY() + 6);
+ context.drawItem(this.icon,this.getX() + 5, this.getY() + 6);
}
RenderSystem.enableDepthTest();
}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCategory.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCategory.java
new file mode 100644
index 00000000..3b402dc9
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCategory.java
@@ -0,0 +1,97 @@
+package me.xmrvizzy.skyblocker.skyblock.rei;
+
+import com.google.common.collect.Lists;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import me.shedaniel.math.Point;
+import me.shedaniel.math.Rectangle;
+import me.shedaniel.rei.api.client.gui.Renderer;
+import me.shedaniel.rei.api.client.gui.widgets.Label;
+import me.shedaniel.rei.api.client.gui.widgets.Slot;
+import me.shedaniel.rei.api.client.gui.widgets.Widget;
+import me.shedaniel.rei.api.client.gui.widgets.Widgets;
+import me.shedaniel.rei.api.client.registry.display.DisplayCategory;
+import me.shedaniel.rei.api.common.category.CategoryIdentifier;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.StringNbtReader;
+import net.minecraft.text.Text;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Skyblock recipe category class for REI
+ */
+public class SkyblockCategory implements DisplayCategory<SkyblockCraftingDisplay> {
+ @Override
+ public CategoryIdentifier<SkyblockCraftingDisplay> getCategoryIdentifier() {
+ return SkyblockerREIClientPlugin.SKYBLOCK;
+ }
+
+ @Override
+ public Text getTitle() {
+ return Text.translatable("key.categories.skyblocker");
+ }
+
+ @Override
+ public Renderer getIcon() {
+ // TODO separate icon from quickNav
+ SkyblockerConfig.ItemData iconItem = SkyblockerConfig.get().quickNav.button7.item;
+ String nbtString = "{id:\"minecraft:" + iconItem.itemName.toLowerCase(Locale.ROOT) + "\",Count:1";
+ if (iconItem.nbt.length() > 2) nbtString += "," + iconItem.nbt;
+ nbtString += "}";
+ try {
+ return EntryStacks.of(ItemStack.fromNbt(StringNbtReader.parse(nbtString)));
+ } catch (CommandSyntaxException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public int getDisplayHeight() {
+ return 73;
+ }
+
+ /**
+ * Draws display for SkyblockCraftingDisplay
+ *
+ * @param display the display
+ * @param bounds the bounds of the display, configurable with overriding the width, height methods.
+ */
+ @Override
+ public List<Widget> setupDisplay(SkyblockCraftingDisplay display, Rectangle bounds) {
+ List<Widget> out = new ArrayList<>();
+ out.add(Widgets.createRecipeBase(bounds));
+
+ Point startPoint;
+ if (!display.getCraftText().isEmpty() && display.getCraftText() != null) {
+ startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 31);
+ }
+ else {
+ startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 26);
+ }
+ Point resultPoint = new Point(startPoint.x + 95, startPoint.y + 19);
+ out.add(Widgets.createArrow(new Point(startPoint.x + 60, startPoint.y + 18)));
+ out.add(Widgets.createResultSlotBackground(resultPoint));
+
+ // Generate Slots
+ List<EntryIngredient> input = display.getInputEntries();
+ List<Slot> slots = Lists.newArrayList();
+ for (int y = 0; y < 3; y++)
+ for (int x = 0; x < 3; x++)
+ slots.add(Widgets.createSlot(new Point(startPoint.x + 1 + x * 18, startPoint.y + 1 + y * 18)).markInput());
+ for (int i = 0; i < input.size(); i++) {
+ slots.get(i).entries(input.get(i)).markInput();
+ }
+ out.addAll(slots);
+ out.add(Widgets.createSlot(resultPoint).entries(display.getOutputEntries().get(0)).disableBackground().markOutput());
+
+ // Add craftingText Label
+ Label craftTextLabel = Widgets.createLabel(new Point(bounds.getCenterX(), startPoint.y + 55), Text.of(display.getCraftText()));
+ out.add(craftTextLabel);
+ return out;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplay.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplay.java
new file mode 100644
index 00000000..5820780c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplay.java
@@ -0,0 +1,39 @@
+package me.xmrvizzy.skyblocker.skyblock.rei;
+
+
+import me.shedaniel.rei.api.common.category.CategoryIdentifier;
+import me.shedaniel.rei.api.common.display.SimpleGridMenuDisplay;
+import me.shedaniel.rei.api.common.display.basic.BasicDisplay;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+
+import java.util.List;
+
+/**
+ * Skyblock Crafting Recipe display class for REI
+ */
+public class SkyblockCraftingDisplay extends BasicDisplay implements SimpleGridMenuDisplay {
+
+ private final String craftText;
+ public SkyblockCraftingDisplay(List<EntryIngredient> input, List<EntryIngredient> output, String craftText) {
+ super(input, output);
+ this.craftText = craftText;
+ }
+ public String getCraftText() {
+ return craftText;
+ }
+
+ @Override
+ public int getWidth() {
+ return 3;
+ }
+
+ @Override
+ public int getHeight() {
+ return 3;
+ }
+
+ @Override
+ public CategoryIdentifier<?> getCategoryIdentifier() {
+ return SkyblockerREIClientPlugin.SKYBLOCK;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplayGenerator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplayGenerator.java
new file mode 100644
index 00000000..fd3f56ee
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockCraftingDisplayGenerator.java
@@ -0,0 +1,67 @@
+package me.xmrvizzy.skyblocker.skyblock.rei;
+
+import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe;
+import net.minecraft.item.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+public class SkyblockCraftingDisplayGenerator implements DynamicDisplayGenerator<SkyblockCraftingDisplay> {
+
+ @Override
+ public Optional<List<SkyblockCraftingDisplay>> getRecipeFor(EntryStack<?> entry) {
+ if (!(entry.getValue() instanceof ItemStack)) return Optional.empty();
+ EntryStack<ItemStack> inputItem = EntryStacks.of((ItemStack) entry.getValue());
+ List<SkyblockCraftingRecipe> filteredRecipes = ItemRegistry.getRecipes()
+ .stream()
+ .filter(recipe -> ItemRegistry.getInternalName(recipe.getResult()).equals(ItemRegistry.getInternalName(inputItem.getValue())))
+ .toList();
+
+ return Optional.of(generateDisplays(filteredRecipes));
+ }
+
+ @Override
+ public Optional<List<SkyblockCraftingDisplay>> getUsageFor(EntryStack<?> entry) {
+ if (!(entry.getValue() instanceof ItemStack)) return Optional.empty();
+ EntryStack<ItemStack> inputItem = EntryStacks.of((ItemStack) entry.getValue());
+ List<SkyblockCraftingRecipe> filteredRecipes = ItemRegistry.getRecipes()
+ .stream()
+ .filter(recipe -> {
+ for (ItemStack item : recipe.getGrid()) {
+ if(!ItemRegistry.getInternalName(item).equals("") && ItemRegistry.getInternalName(item).equals(ItemRegistry.getInternalName(inputItem.getValue())))
+ return true;
+ }
+ return false;
+ })
+ .toList();
+ return Optional.of(generateDisplays(filteredRecipes));
+ }
+
+ /**
+ * Generate Displays from a list of recipes
+ */
+ private List<SkyblockCraftingDisplay> generateDisplays(List<SkyblockCraftingRecipe> recipes) {
+ List<SkyblockCraftingDisplay> displays = new ArrayList<>();
+ for (SkyblockCraftingRecipe recipe : recipes) {
+ List<EntryIngredient> inputs = new ArrayList<>();
+ List<EntryIngredient> outputs = new ArrayList<>();
+
+ ArrayList<EntryStack<ItemStack>> inputEntryStacks = new ArrayList<>();
+ recipe.getGrid().forEach((item) -> inputEntryStacks.add(EntryStacks.of(item)));
+
+ for (EntryStack<ItemStack> entryStack : inputEntryStacks) {
+ inputs.add(EntryIngredient.of(entryStack));
+ }
+ outputs.add(EntryIngredient.of(EntryStacks.of(recipe.getResult())));
+
+ displays.add(new SkyblockCraftingDisplay(inputs, outputs, recipe.getCraftText()));
+ }
+ return displays;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockerREIClientPlugin.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockerREIClientPlugin.java
new file mode 100644
index 00000000..5f43ca0e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rei/SkyblockerREIClientPlugin.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.rei;
+
+import me.shedaniel.rei.api.client.plugins.REIClientPlugin;
+import me.shedaniel.rei.api.client.registry.category.CategoryRegistry;
+import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
+import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
+import me.shedaniel.rei.api.common.category.CategoryIdentifier;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+
+import java.util.ArrayList;
+
+/**
+ * REI integration
+ */
+public class SkyblockerREIClientPlugin implements REIClientPlugin {
+ public static final CategoryIdentifier<SkyblockCraftingDisplay> SKYBLOCK = CategoryIdentifier.of(SkyblockerMod.NAMESPACE, "skyblock");
+
+ @Override
+ public void registerCategories(CategoryRegistry categoryRegistry) {
+ categoryRegistry.addWorkstations(SKYBLOCK, EntryStacks.of(Items.CRAFTING_TABLE));
+ categoryRegistry.add(new SkyblockCategory());
+ }
+
+ @Override
+ public void registerDisplays(DisplayRegistry displayRegistry) {
+ displayRegistry.registerDisplayGenerator(SKYBLOCK, new SkyblockCraftingDisplayGenerator());
+ }
+
+ @Override
+ public void registerEntries(EntryRegistry entryRegistry) {
+ ArrayList<EntryStack<ItemStack>> entries = new ArrayList<>();
+ ItemRegistry.getRecipes().forEach(recipe -> entries.add(EntryStacks.of(recipe.getResult())));
+ entryRegistry.addEntries(entries);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java
new file mode 100644
index 00000000..7376c896
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java
@@ -0,0 +1,79 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraft.scoreboard.ScoreboardObjective;
+import net.minecraft.scoreboard.ScoreboardPlayerScore;
+import net.minecraft.scoreboard.Team;
+import net.minecraft.text.Text;
+import net.minecraft.text.TextColor;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class EffigyWaypoints {
+ private static final Logger LOGGER = LoggerFactory.getLogger(EffigyWaypoints.class);
+ private static final List<BlockPos> effigies = List.of(
+ new BlockPos(150, 79, 95), //Effigy 1
+ new BlockPos(193, 93, 119), //Effigy 2
+ new BlockPos(235, 110, 147), //Effigy 3
+ new BlockPos(293, 96, 134), //Effigy 4
+ new BlockPos(262, 99, 94), //Effigy 5
+ new BlockPos(240, 129, 118) //Effigy 6
+ );
+ private static final List<BlockPos> unBrokenEffigies = new ArrayList<>();
+
+ protected static void updateEffigies() {
+ if (!SkyblockerConfig.get().slayer.vampireSlayer.enableEffigyWaypoints || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) return;
+
+ unBrokenEffigies.clear();
+ try {
+ ClientPlayerEntity player = MinecraftClient.getInstance().player;
+ if (player == null) return;
+ Scoreboard scoreboard = player.getScoreboard();
+ ScoreboardObjective objective = scoreboard.getObjectiveForSlot(1);
+ for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) {
+ Team team = scoreboard.getPlayerTeam(score.getPlayerName());
+ if (team != null) {
+ String line = team.getPrefix().getString() + team.getSuffix().getString();
+ if (line.contains("Effigies")) {
+ List<Text> newList = new ArrayList<>(team.getPrefix().getSiblings());
+ newList.addAll(team.getSuffix().getSiblings());
+ for (int i = 1; i < newList.size(); i++) {
+ if (newList.get(i).getStyle().getColor() == TextColor.parse("gray")) {
+ unBrokenEffigies.add(effigies.get(i - 1));
+ }
+ }
+ }
+ }
+ }
+ } catch (NullPointerException e) {
+ LOGGER.error("[Skyblocker] Error while updating effigies.", e);
+ }
+ }
+
+ protected static void render(WorldRenderContext context) {
+ if (SkyblockerConfig.get().slayer.vampireSlayer.enableEffigyWaypoints && Utils.getLocation().contains("Stillgore Château")) {
+ for (BlockPos effigy : unBrokenEffigies) {
+ float[] colorComponents = DyeColor.RED.getColorComponents();
+ if (SkyblockerConfig.get().slayer.vampireSlayer.compactEffigyWaypoints) {
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy.down(6), colorComponents, 0.5F);
+ } else {
+ RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy, colorComponents, 0.5F);
+ for (int i = 1; i < 6; i++) {
+ RenderHelper.renderFilledThroughWalls(context, effigy.down(i), colorComponents, 0.5F - (0.075F * i));
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java
new file mode 100644
index 00000000..fed34796
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java
@@ -0,0 +1,27 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.title.Title;
+import me.xmrvizzy.skyblocker.utils.title.TitleContainer;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.util.Formatting;
+
+public class HealingMelonIndicator {
+ private static final Title title = new Title("skyblocker.rift.healNow", Formatting.DARK_RED);
+
+ public static void updateHealth(MinecraftClient client) {
+ if (!SkyblockerConfig.get().slayer.vampireSlayer.enableHealingMelonIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) {
+ TitleContainer.removeTitle(title);
+ return;
+ }
+ ClientPlayerEntity player = client.player;
+ if (player != null && player.getHealth() <= SkyblockerConfig.get().slayer.vampireSlayer.healingMelonHealthThreshold * 2F) {
+ RenderHelper.displayInTitleContainerAndPlaySound(title);
+ } else {
+ TitleContainer.removeTitle(title);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java
new file mode 100644
index 00000000..38f6b018
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java
@@ -0,0 +1,42 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.SlayerUtils;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.title.Title;
+import me.xmrvizzy.skyblocker.utils.title.TitleContainer;
+import net.minecraft.block.Blocks;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.Entity;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.math.BlockPos;
+
+public class ManiaIndicator {
+ private static final Title title = new Title("skyblocker.rift.mania", Formatting.RED);
+
+ protected static void updateMania() {
+ if (!SkyblockerConfig.get().slayer.vampireSlayer.enableManiaIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !(Utils.getLocation().contains("Stillgore Château")) || !SlayerUtils.isInSlayer()) {
+ TitleContainer.removeTitle(title);
+ return;
+ }
+
+ Entity slayerEntity = SlayerUtils.getSlayerEntity();
+ if (slayerEntity == null) return;
+
+ boolean anyMania = false;
+ for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) {
+ if (entity.getDisplayName().toString().contains("MANIA")) {
+ anyMania = true;
+ BlockPos pos = MinecraftClient.getInstance().player.getBlockPos().down();
+ boolean isGreen = MinecraftClient.getInstance().world.getBlockState(pos).getBlock() == Blocks.GREEN_TERRACOTTA;
+ title.setText(Text.translatable("skyblocker.rift.mania").formatted(isGreen ? Formatting.GREEN : Formatting.RED));
+ RenderHelper.displayInTitleContainerAndPlaySound(title);
+ }
+ }
+ if (!anyMania) {
+ TitleContainer.removeTitle(title);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java
new file mode 100644
index 00000000..32551179
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java
@@ -0,0 +1,88 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+public class MirrorverseWaypoints {
+ private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker");
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+ private static final Identifier WAYPOINTS_JSON = new Identifier(SkyblockerMod.NAMESPACE, "mirrorverse_waypoints.json");
+ private static final BlockPos[] LAVA_PATH_WAYPOINTS = new BlockPos[107];
+ private static final BlockPos[] UPSIDE_DOWN_WAYPOINTS = new BlockPos[66];
+ private static final BlockPos[] TURBULATOR_WAYPOINTS = new BlockPos[27];
+ private static final float[] COLOR_COMPONENTS = DyeColor.RED.getColorComponents();
+
+ static {
+ loadWaypoints();
+ }
+
+ /**
+ * Loads the waypoint locations into memory
+ */
+ private static void loadWaypoints() {
+ try (BufferedReader reader = CLIENT.getResourceManager().openAsReader(WAYPOINTS_JSON)) {
+ JsonObject file = JsonParser.parseReader(reader).getAsJsonObject();
+ JsonArray sections = file.get("sections").getAsJsonArray();
+
+ /// Lava Path
+ JsonArray lavaPathWaypoints = sections.get(0).getAsJsonObject().get("waypoints").getAsJsonArray();
+
+ for (int i = 0; i < lavaPathWaypoints.size(); i++) {
+ JsonObject point = lavaPathWaypoints.get(i).getAsJsonObject();
+ LAVA_PATH_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt());
+ }
+
+ /// Upside Down Parkour
+ JsonArray upsideDownParkourWaypoints = sections.get(1).getAsJsonObject().get("waypoints").getAsJsonArray();
+
+ for (int i = 0; i < upsideDownParkourWaypoints.size(); i++) {
+ JsonObject point = upsideDownParkourWaypoints.get(i).getAsJsonObject();
+ UPSIDE_DOWN_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt());
+ }
+
+ /// Turbulator Parkour
+ JsonArray turbulatorParkourWaypoints = sections.get(2).getAsJsonObject().get("waypoints").getAsJsonArray();
+
+ for (int i = 0; i < turbulatorParkourWaypoints.size(); i++) {
+ JsonObject point = turbulatorParkourWaypoints.get(i).getAsJsonObject();
+ TURBULATOR_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt());
+ }
+
+ } catch (IOException e) {
+ LOGGER.info("[Skyblocker] Mirrorverse Waypoints failed to load ;(");
+ e.printStackTrace();
+ }
+ }
+
+ protected static void render(WorldRenderContext wrc) {
+ //I would also check for the mirrorverse location but the scoreboard stuff is not performant at all...
+ if (Utils.isInTheRift() && SkyblockerConfig.get().locations.rift.mirrorverseWaypoints) {
+ for (BlockPos pos : LAVA_PATH_WAYPOINTS) {
+ RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f);
+ }
+
+ for (BlockPos pos : UPSIDE_DOWN_WAYPOINTS) {
+ RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f);
+ }
+
+ for (BlockPos pos : TURBULATOR_WAYPOINTS) {
+ RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f);
+ }
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java
new file mode 100644
index 00000000..90fc436d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.SlayerUtils;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.title.Title;
+import me.xmrvizzy.skyblocker.utils.title.TitleContainer;
+import net.minecraft.entity.Entity;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class StakeIndicator {
+ private static final Title title = new Title("skyblocker.rift.stakeNow",Formatting.RED);
+
+ protected static void updateStake() {
+ if (!SkyblockerConfig.get().slayer.vampireSlayer.enableSteakStakeIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château") || !SlayerUtils.isInSlayer()) {
+ TitleContainer.removeTitle(title);
+ return;
+ }
+ Entity slayerEntity = SlayerUtils.getSlayerEntity();
+ if (slayerEntity != null && slayerEntity.getDisplayName().toString().contains("҉")) {
+ RenderHelper.displayInTitleContainerAndPlaySound(title);
+ } else {
+ TitleContainer.removeTitle(title);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java
new file mode 100644
index 00000000..5ca89dcf
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java
@@ -0,0 +1,21 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
+
+public class TheRift {
+ /**
+ * @see me.xmrvizzy.skyblocker.utils.Utils#isInTheRift() Utils#isInTheRift().
+ */
+ public static final String LOCATION = "rift";
+
+ public static void init() {
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render);
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render);
+ SkyblockerMod.getInstance().scheduler.scheduleCyclic(EffigyWaypoints::updateEffigies, SkyblockerConfig.get().slayer.vampireSlayer.effigyUpdateFrequency);
+ SkyblockerMod.getInstance().scheduler.scheduleCyclic(TwinClawsIndicator::updateIce, SkyblockerConfig.get().slayer.vampireSlayer.holyIceUpdateFrequency);
+ SkyblockerMod.getInstance().scheduler.scheduleCyclic(ManiaIndicator::updateMania, SkyblockerConfig.get().slayer.vampireSlayer.maniaUpdateFrequency);
+ SkyblockerMod.getInstance().scheduler.scheduleCyclic(StakeIndicator::updateStake, SkyblockerConfig.get().slayer.vampireSlayer.steakStakeUpdateFrequency);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java
new file mode 100644
index 00000000..f36b97df
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.rift;
+
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderHelper;
+import me.xmrvizzy.skyblocker.utils.SlayerUtils;
+import me.xmrvizzy.skyblocker.utils.Utils;
+import me.xmrvizzy.skyblocker.utils.title.Title;
+import me.xmrvizzy.skyblocker.utils.title.TitleContainer;
+import net.minecraft.entity.Entity;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class TwinClawsIndicator {
+ private static final Title title = new Title("skyblocker.rift.iceNow",Formatting.AQUA);
+ private static boolean scheduled = false;
+
+ protected static void updateIce() {
+ if (!SkyblockerConfig.get().slayer.vampireSlayer.enableHolyIceIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !(Utils.getLocation().contains("Stillgore Château")) || !SlayerUtils.isInSlayer()) {
+ TitleContainer.removeTitle(title);
+ return;
+ }
+
+ Entity slayerEntity = SlayerUtils.getSlayerEntity();
+ if (slayerEntity == null) return;
+
+ boolean anyClaws = false;
+ for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) {
+ if (entity.getDisplayName().toString().contains("TWINCLAWS")) {
+ anyClaws = true;
+ if (!TitleContainer.containsTitle(title) && !scheduled) {
+ scheduled = true;
+ SkyblockerMod.getInstance().scheduler.schedule(() -> {
+ RenderHelper.displayInTitleContainerAndPlaySound(title);
+ scheduled = false;
+ }, SkyblockerConfig.get().slayer.vampireSlayer.holyIceIndicatorTickDelay);
+ }
+ }
+ }
+ if (!anyClaws) {
+ TitleContainer.removeTitle(title);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
new file mode 100644
index 00000000..6d90b269
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud;
+
+import org.lwjgl.glfw.GLFW;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
+import net.minecraft.client.option.KeyBinding;
+import net.minecraft.client.util.InputUtil;
+
+public class TabHud {
+
+ public static KeyBinding playerTgl;
+ public static KeyBinding genericTgl;
+ // public static KeyBinding mapTgl;
+ public static KeyBinding defaultTgl;
+
+ public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Tab HUD");
+
+ public static void init() {
+
+ playerTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.playerTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_B,
+ "key.categories.skyblocker"));
+ genericTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.genericTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_N,
+ "key.categories.skyblocker"));
+ // mapTgl = KeyBindingHelper.registerKeyBinding(
+ // new KeyBinding("key.tabhud.mapTgl",
+ // InputUtil.Type.KEYSYM,
+ // GLFW.GLFW_KEY_LEFT_ALT,
+ // "key.categories.skyblocker"));
+ defaultTgl = KeyBindingHelper.registerKeyBinding(
+ new KeyBinding("key.skyblocker.defaultTgl",
+ InputUtil.Type.KEYSYM,
+ GLFW.GLFW_KEY_M,
+ "key.categories.skyblocker"));
+
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java
new file mode 100644
index 00000000..5c302eb3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/EmptyScreen.java
@@ -0,0 +1,16 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EmptyWidget;
+
+import net.minecraft.text.Text;
+
+public class EmptyScreen extends Screen {
+
+ public EmptyScreen(int w, int h, Text footer) {
+ super(w, h);
+ EmptyWidget ew = new EmptyWidget();
+ this.center(ew);
+ this.addWidget(ew);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
new file mode 100644
index 00000000..6d06c637
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/Screen.java
@@ -0,0 +1,230 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens;
+
+import java.util.ArrayList;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo.GardenInfoScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo.GenericInfoScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo.GenericRiftInfoScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.CrimsonIsleScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.DungeonHubScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.DungeonScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.FarmingServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.GardenScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.GenericServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.GuestServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.HomeServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.HubServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.MineServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.ParkServerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main.RiftScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.DungeonPlayerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.GuestPlayerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.HomePlayerScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList.PlayerListScreen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerLocator;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.text.Text;
+
+public class Screen {
+
+ private ArrayList<Widget> widgets = new ArrayList<>();
+ private int w, h;
+
+ public Screen(int w, int h) {
+ float scale = SkyblockerConfig.get().general.tabHud.tabHudScale / 100f;
+ this.w = (int) (w / scale);
+ this.h = (int) (h / scale);
+ }
+
+ public static Screen getCorrect(int w, int h, Text footer) {
+ if (TabHud.genericTgl.isPressed()) {
+ return Screen.correctGenericScrn(w, h, footer);
+ } else if (TabHud.playerTgl.isPressed()) {
+ return Screen.correctPlayerScrn(w, h, footer);
+ } else {
+ return Screen.correctMainScrn(w, h, footer);
+ }
+ }
+
+ private static Screen correctGenericScrn(int w, int h, Text footer) {
+ return switch (PlayerLocator.getPlayerLocation()) {
+ case GARDEN -> new GardenInfoScreen(w, h, footer); // ok
+ case THE_RIFT -> new GenericRiftInfoScreen(w, h, footer);
+ case UNKNOWN -> new EmptyScreen(w, h, footer); // ok
+ default -> new GenericInfoScreen(w, h, footer); // ok
+ };
+ }
+
+ private static Screen correctPlayerScrn(int w, int h, Text footer) {
+ return switch (PlayerLocator.getPlayerLocation()) {
+ case GUEST_ISLAND -> new GuestPlayerScreen(w, h, footer); // ok
+ case HOME_ISLAND, GARDEN -> new HomePlayerScreen(w, h, footer); // ok for 1 player
+ case DUNGEON -> new DungeonPlayerScreen(w, h, footer);
+ case UNKNOWN -> new EmptyScreen(w, h, footer); // ok
+ default -> new PlayerListScreen(w, h, footer); // ok
+ };
+ }
+
+ private static Screen correctMainScrn(int w, int h, Text footer) {
+ return switch (PlayerLocator.getPlayerLocation()) {
+ case PARK -> new ParkServerScreen(w, h, footer); // ok
+ case HUB -> new HubServerScreen(w, h, footer); // ok when fire sale incoming
+ case HOME_ISLAND -> new HomeServerScreen(w, h, footer); // ok
+ case GUEST_ISLAND -> new GuestServerScreen(w, h, footer); // ok
+ case CRYSTAL_HOLLOWS, DWARVEN_MINES -> new MineServerScreen(w, h, footer); // ok, TODO active forge
+ case FARMING_ISLAND -> new FarmingServerScreen(w, h, footer);
+ case DUNGEON_HUB -> new DungeonHubScreen(w, h, footer); // ok
+ case DUNGEON -> new DungeonScreen(w, h, footer); // ok
+ case CRIMSON_ISLE -> new CrimsonIsleScreen(w, h, footer);
+ case GARDEN -> new GardenScreen(w, h, footer); // ok
+ case THE_RIFT -> new RiftScreen(w, h, footer);
+ case UNKNOWN -> new EmptyScreen(w, h, footer); // ok
+ default -> new GenericServerScreen(w, h, footer); // ok
+ };
+ }
+
+ /**
+ * Add a widget to this screen
+ */
+ public void addWidget(Widget w) {
+ widgets.add(w);
+ }
+
+ /**
+ * Add many widgets to this screen
+ */
+ public void addWidgets(Widget... ws) {
+ for (Widget w : ws) {
+ widgets.add(w);
+ }
+ }
+
+ public void render(DrawContext context) {
+ for (Widget w : widgets) {
+ w.render(context);
+ }
+ }
+
+ /**
+ * Stack these widgets on top of each other as determined by the lists's order
+ */
+ public void stackWidgetsH(Widget... list) {
+ int compHeight = -5;
+ for (Widget wid : list) {
+ compHeight += wid.getHeight() + 5;
+ }
+
+ int y = (h - compHeight) / 2;
+ for (Widget wid : list) {
+ wid.setY(y);
+ y += wid.getHeight() + 5;
+ }
+ }
+
+ /**
+ * Arrange these widgets next to each other as determined by the lists's order
+ */
+ public void stackWidgetsW(Widget... list) {
+ // TODO not centered
+ int compWidth = -5;
+ for (Widget wid : list) {
+ compWidth += wid.getWidth() + 5;
+ }
+
+ int x = (w - compWidth) / 2;
+ for (Widget wid : list) {
+ wid.setX(x);
+ x += wid.getWidth() + 5;
+ }
+ }
+
+ /**
+ * Center a widget vertically, keeping X pos
+ */
+ public void centerH(Widget wid) {
+ wid.setY((h - wid.getHeight()) / 2);
+ }
+
+ /**
+ * Center a widget horizontally, keeping Y pos
+ */
+ public void centerW(Widget wid) {
+ wid.setX((w - wid.getWidth()) / 2);
+ }
+
+ /**
+ * Center a widget vertically and horizontally
+ */
+ public void center(Widget wid) {
+ this.centerH(wid);
+ this.centerW(wid);
+ }
+
+ /**
+ * Let a widget's left border be on the screen's center, keeping Y pos
+ */
+ public void offCenterL(Widget wid) {
+ int wHalf = this.w / 2;
+ wid.setX(wHalf - 3 - wid.getWidth());
+ }
+
+ /**
+ * Let a widget's right border be on the screen's center, keeping Y pos
+ */
+ public void offCenterR(Widget wid) {
+ int wHalf = this.w / 2;
+ wid.setX(wHalf + 3);
+ }
+
+ public void collideAgainstL(Widget w, Widget... others) {
+ int yMin = w.getY();
+ int yMax = w.getY() + w.getHeight();
+
+ int xCor = this.w / 2;
+
+ // assume others to be sorted top-bottom.
+ for (Widget other : others) {
+ if (other.getY() + other.getHeight() + 5 < yMin) {
+ // too high, next one
+ continue;
+ }
+
+ if (other.getY() - 5 > yMax) {
+ // too low, no more collisions possible
+ break;
+ }
+
+ int xPos = other.getX() - 5 - w.getWidth();
+ xCor = Math.min(xCor, xPos);
+ }
+ w.setX(xCor);
+ }
+
+ public void collideAgainstR(Widget w, Widget... others) {
+ int yMin = w.getY();
+ int yMax = w.getY() + w.getHeight();
+
+ int xCor = this.w / 2;
+
+ // assume others to be sorted top-bottom.
+ for (Widget other : others) {
+ if (other.getY() + other.getHeight() + 5 < yMin) {
+ // too high, next one
+ continue;
+ }
+
+ if (other.getY() - 5 > yMax) {
+ // too low, no more collisions possible
+ break;
+ }
+
+ int xPos = other.getX() + other.getWidth() + 5;
+ xCor = Math.max(xCor, xPos);
+ }
+ w.setX(xCor);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java
new file mode 100644
index 00000000..0bb12c8e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GardenInfoScreen.java
@@ -0,0 +1,51 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CookieWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EffectWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EventWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GardenSkillsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.JacobsContestWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ProfileWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.UpgradeWidget;
+
+import net.minecraft.text.Text;
+
+public class GardenInfoScreen extends Screen {
+
+ public GardenInfoScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ GardenSkillsWidget gsw = new GardenSkillsWidget();
+ EventWidget evw = new EventWidget(true);
+ UpgradeWidget uw = new UpgradeWidget(f);
+
+ ProfileWidget pw = new ProfileWidget();
+ EffectWidget efw = new EffectWidget(f);
+
+ JacobsContestWidget jcw = new JacobsContestWidget();
+ CookieWidget cw = new CookieWidget(f);
+
+ // layout code incoming
+ this.stackWidgetsH(gsw, evw, uw);
+ this.stackWidgetsH(pw, efw);
+ this.stackWidgetsH(jcw, cw);
+
+ this.centerW(gsw);
+ this.centerW(evw);
+ this.centerW(uw);
+
+ this.collideAgainstL(pw, gsw, evw, uw);
+ this.collideAgainstL(efw, gsw, evw, uw);
+
+ this.collideAgainstR(jcw, gsw, evw, uw);
+ this.collideAgainstR(cw, gsw, evw, uw);
+
+ this.addWidgets(gsw, evw, uw, pw, efw, jcw, cw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java
new file mode 100644
index 00000000..046a9313
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericInfoScreen.java
@@ -0,0 +1,48 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CookieWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EffectWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ElectionWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EventWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ProfileWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.SkillsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.UpgradeWidget;
+
+import net.minecraft.text.Text;
+
+public class GenericInfoScreen extends Screen {
+
+ public GenericInfoScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ SkillsWidget sw = new SkillsWidget();
+ EventWidget evw = new EventWidget(false);
+ UpgradeWidget uw = new UpgradeWidget(f);
+
+ ProfileWidget pw = new ProfileWidget();
+ EffectWidget efw = new EffectWidget(f);
+
+ ElectionWidget elw = new ElectionWidget();
+ CookieWidget cw = new CookieWidget(f);
+
+ this.stackWidgetsH(sw, evw, uw);
+ this.stackWidgetsH(pw, efw);
+ this.stackWidgetsH(elw, cw);
+
+ this.centerW(sw);
+ this.centerW(evw);
+ this.centerW(uw);
+
+ this.collideAgainstL(pw, sw, evw, uw);
+ this.collideAgainstL(efw, sw, evw, uw);
+
+ this.collideAgainstR(elw, sw, evw, uw);
+ this.collideAgainstR(cw, sw, evw, uw);
+
+ this.addWidgets(sw, evw, uw, pw, efw, elw, cw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericRiftInfoScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericRiftInfoScreen.java
new file mode 100644
index 00000000..9821b5a3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/genericInfo/GenericRiftInfoScreen.java
@@ -0,0 +1,38 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.genericInfo;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftProfileWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftStatsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.ShenWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CookieWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.AdvertisementWidget;
+import net.minecraft.text.Text;
+
+public class GenericRiftInfoScreen extends Screen {
+
+ public GenericRiftInfoScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ RiftProfileWidget profile = new RiftProfileWidget();
+ RiftStatsWidget stats = new RiftStatsWidget();
+ ShenWidget shen = new ShenWidget();
+
+ CookieWidget cookie = new CookieWidget(f);
+ AdvertisementWidget advertisement = new AdvertisementWidget();
+
+ this.stackWidgetsH(stats, advertisement);
+ this.stackWidgetsH(profile, shen, cookie);
+
+ this.offCenterL(stats);
+ this.offCenterL(advertisement);
+
+ this.offCenterR(profile);
+ this.offCenterR(shen);
+ this.offCenterR(cookie);
+
+ this.addWidgets(profile, stats, shen, cookie, advertisement);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java
new file mode 100644
index 00000000..6e6f563b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/CrimsonIsleScreen.java
@@ -0,0 +1,32 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.QuestWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ReputationWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.VolcanoWidget;
+
+import net.minecraft.text.Text;
+
+public class CrimsonIsleScreen extends Screen {
+
+ public CrimsonIsleScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ ReputationWidget rw = new ReputationWidget();
+ QuestWidget qw = new QuestWidget();
+ VolcanoWidget vw = new VolcanoWidget();
+
+ this.stackWidgetsH(sw, rw);
+ this.stackWidgetsH(qw, vw);
+ this.offCenterL(sw);
+ this.offCenterL(rw);
+ this.offCenterR(vw);
+ this.offCenterR(qw);
+ this.addWidgets(sw, rw, qw, vw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java
new file mode 100644
index 00000000..5db461af
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonHubScreen.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EssenceWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.text.Text;
+
+public class DungeonHubScreen extends Screen{
+
+ public DungeonHubScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ EssenceWidget ew = new EssenceWidget();
+
+ this.centerW(sw);
+ this.centerW(ew);
+ this.stackWidgetsH(sw, ew);
+ this.addWidget(ew);
+ this.addWidget(sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java
new file mode 100644
index 00000000..852ee876
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/DungeonScreen.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonBuffWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonDeathWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonDownedWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPuzzleWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonSecretWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonServerWidget;
+import net.minecraft.text.Text;
+
+public class DungeonScreen extends Screen {
+
+ public DungeonScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ String f = footer.getString();
+
+ DungeonDownedWidget ddow = new DungeonDownedWidget();
+ DungeonDeathWidget ddew = new DungeonDeathWidget();
+ DungeonSecretWidget dscw = new DungeonSecretWidget();
+ DungeonServerWidget dsrw = new DungeonServerWidget();
+ DungeonPuzzleWidget dpuw = new DungeonPuzzleWidget();
+ DungeonBuffWidget dbw = new DungeonBuffWidget(f);
+
+ this.offCenterL(ddow);
+ this.offCenterL(ddew);
+ this.offCenterL(dbw);
+ this.offCenterR(dsrw);
+ this.offCenterR(dpuw);
+ this.offCenterR(dscw);
+
+ this.stackWidgetsH(ddow, ddew, dbw);
+ this.stackWidgetsH(dsrw, dpuw, dscw);
+
+ this.addWidgets(ddow, ddew, dscw, dsrw, dpuw, dbw);
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java
new file mode 100644
index 00000000..02c81f23
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/FarmingServerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.TrapperWidget;
+
+
+import net.minecraft.text.Text;
+
+public class FarmingServerScreen extends Screen{
+
+ public FarmingServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ TrapperWidget tw = new TrapperWidget();
+
+ this.centerW(sw);
+ this.centerW(tw);
+ this.stackWidgetsH(sw, tw);
+ this.addWidgets(tw, sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java
new file mode 100644
index 00000000..ae5b642f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GardenScreen.java
@@ -0,0 +1,23 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ComposterWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GardenServerWidget;
+import net.minecraft.text.Text;
+
+public class GardenScreen extends Screen{
+
+ public GardenScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ GardenServerWidget gsw = new GardenServerWidget();
+ ComposterWidget cw = new ComposterWidget();
+
+ this.stackWidgetsH(gsw, cw);
+ this.centerW(gsw);
+ this.centerW(cw);
+ this.addWidgets(gsw, cw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java
new file mode 100644
index 00000000..a89563db
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GenericServerScreen.java
@@ -0,0 +1,21 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.text.Text;
+
+public class GenericServerScreen extends Screen {
+
+ public GenericServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+
+ this.center(sw);
+ this.addWidget(sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java
new file mode 100644
index 00000000..57d7a199
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/GuestServerScreen.java
@@ -0,0 +1,22 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.GuestServerWidget;
+
+
+import net.minecraft.text.Text;
+
+public class GuestServerScreen extends Screen{
+
+ public GuestServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ GuestServerWidget gsw = new GuestServerWidget();
+
+ this.center(gsw);
+ this.addWidget(gsw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java
new file mode 100644
index 00000000..e61ba4b0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HomeServerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandServerWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.MinionWidget;
+
+
+import net.minecraft.text.Text;
+
+public class HomeServerScreen extends Screen {
+
+ public HomeServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ IslandServerWidget isw = new IslandServerWidget();
+ MinionWidget mw = new MinionWidget();
+
+ this.centerH(isw);
+ this.centerH(mw);
+ this.stackWidgetsW(isw, mw);
+ this.addWidgets(isw, mw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java
new file mode 100644
index 00000000..e2857f7e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/HubServerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.FireSaleWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+
+import net.minecraft.text.Text;
+
+public class HubServerScreen extends Screen {
+
+ public HubServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ FireSaleWidget fsw = new FireSaleWidget();
+
+ this.centerW(sw);
+ this.centerW(fsw);
+ this.stackWidgetsH(sw, fsw);
+ this.addWidgets(sw, fsw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java
new file mode 100644
index 00000000..616c3e82
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/MineServerScreen.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.CommsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ForgeWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.PowderWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ServerWidget;
+
+import net.minecraft.text.Text;
+
+public class MineServerScreen extends Screen {
+
+ public MineServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ServerWidget sw = new ServerWidget();
+ PowderWidget pw = new PowderWidget();
+ CommsWidget cw = new CommsWidget();
+ ForgeWidget fw = new ForgeWidget();
+
+ this.stackWidgetsH(sw, cw);
+ this.stackWidgetsH(fw, pw);
+ this.offCenterL(sw);
+ this.offCenterL(cw);
+ this.offCenterR(pw);
+ this.offCenterR(fw);
+ this.addWidgets(fw, cw, pw, sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java
new file mode 100644
index 00000000..aa65d946
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/ParkServerScreen.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ParkServerWidget;
+
+import net.minecraft.text.Text;
+
+public class ParkServerScreen extends Screen{
+
+ public ParkServerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ ParkServerWidget sw = new ParkServerWidget();
+
+ this.center(sw);
+ this.addWidget(sw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/RiftScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/RiftScreen.java
new file mode 100644
index 00000000..d63bcf62
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/main/RiftScreen.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.main;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftProgressWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.GoodToKnowWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift.RiftServerInfoWidget;
+
+
+import net.minecraft.text.Text;
+
+public class RiftScreen extends Screen {
+
+ public RiftScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ RiftProgressWidget rftProg = new RiftProgressWidget();
+ GoodToKnowWidget gtk = new GoodToKnowWidget();
+ RiftServerInfoWidget si = new RiftServerInfoWidget();
+
+ this.stackWidgetsH(si, gtk);
+ this.stackWidgetsH(rftProg);
+ this.offCenterL(si);
+ this.offCenterL(gtk);
+ this.offCenterR(rftProg);
+ this.addWidgets(si, gtk, rftProg);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java
new file mode 100644
index 00000000..2567da13
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/DungeonPlayerScreen.java
@@ -0,0 +1,29 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget;
+import net.minecraft.text.Text;
+
+public class DungeonPlayerScreen extends Screen {
+
+ public DungeonPlayerScreen(int w, int h, Text footer) {
+
+ super(w, h);
+
+ DungeonPlayerWidget dpw1 = new DungeonPlayerWidget(1);
+ DungeonPlayerWidget dpw2 = new DungeonPlayerWidget(2);
+ DungeonPlayerWidget dpw3 = new DungeonPlayerWidget(3);
+ DungeonPlayerWidget dpw4 = new DungeonPlayerWidget(4);
+ DungeonPlayerWidget dpw5 = new DungeonPlayerWidget(5);
+
+ this.offCenterL(dpw1);
+ this.offCenterL(dpw2);
+ this.offCenterL(dpw3);
+ this.offCenterR(dpw4);
+ this.offCenterR(dpw5);
+ this.stackWidgetsH(dpw1, dpw2, dpw3);
+ this.stackWidgetsH(dpw4, dpw5);
+ this.addWidgets(dpw1, dpw2, dpw3, dpw4, dpw5);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java
new file mode 100644
index 00000000..5a9733cc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/GuestPlayerScreen.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandGuestsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandOwnersWidget;
+
+
+import net.minecraft.text.Text;
+
+public class GuestPlayerScreen extends Screen{
+
+ public GuestPlayerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ IslandGuestsWidget igw = new IslandGuestsWidget();
+ IslandOwnersWidget iow = new IslandOwnersWidget();
+
+ this.centerH(iow);
+ this.centerH(igw);
+ this.stackWidgetsW(igw, iow);
+ this.addWidgets(iow, igw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java
new file mode 100644
index 00000000..2a159ecc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/HomePlayerScreen.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandGuestsWidget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.IslandSelfWidget;
+
+
+import net.minecraft.text.Text;
+
+public class HomePlayerScreen extends Screen {
+
+ public HomePlayerScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ IslandSelfWidget isw = new IslandSelfWidget();
+ IslandGuestsWidget igw = new IslandGuestsWidget();
+
+ this.centerH(isw);
+ this.centerH(igw);
+ this.stackWidgetsW(isw, igw);
+ this.addWidgets(isw, igw);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java
new file mode 100644
index 00000000..5db01512
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screens/playerList/PlayerListScreen.java
@@ -0,0 +1,20 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.screens.playerList;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.screens.Screen;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.PlayerListWidget;
+
+import net.minecraft.text.Text;
+
+public class PlayerListScreen extends Screen {
+
+ public PlayerListScreen(int w, int h, Text footer) {
+ super(w, h);
+
+ PlayerListWidget plw = new PlayerListWidget();
+
+ this.center(plw);
+ this.addWidget(plw);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
new file mode 100644
index 00000000..97237769
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+
+/**
+ * Stores convenient shorthands for common ItemStack definitions
+ */
+public class Ico {
+ public static final ItemStack MAP = new ItemStack(Items.FILLED_MAP);
+ public static final ItemStack NTAG = new ItemStack(Items.NAME_TAG);
+ public static final ItemStack EMERALD = new ItemStack(Items.EMERALD);
+ public static final ItemStack CLOCK = new ItemStack(Items.CLOCK);
+ public static final ItemStack DIASWORD = new ItemStack(Items.DIAMOND_SWORD);
+ public static final ItemStack DBUSH = new ItemStack(Items.DEAD_BUSH);
+ public static final ItemStack VILLAGER = new ItemStack(Items.VILLAGER_SPAWN_EGG);
+ public static final ItemStack MOREGOLD = new ItemStack(Items.GOLDEN_APPLE);
+ public static final ItemStack COMPASS = new ItemStack(Items.COMPASS);
+ public static final ItemStack SUGAR = new ItemStack(Items.SUGAR);
+ public static final ItemStack HOE = new ItemStack(Items.IRON_HOE);
+ public static final ItemStack GOLD = new ItemStack(Items.GOLD_INGOT);
+ public static final ItemStack BONE = new ItemStack(Items.BONE);
+ public static final ItemStack SIGN = new ItemStack(Items.OAK_SIGN);
+ public static final ItemStack FISH_ROD = new ItemStack(Items.FISHING_ROD);
+ public static final ItemStack SWORD = new ItemStack(Items.IRON_SWORD);
+ public static final ItemStack LANTERN = new ItemStack(Items.LANTERN);
+ public static final ItemStack COOKIE = new ItemStack(Items.COOKIE);
+ public static final ItemStack POTION = new ItemStack(Items.POTION);
+ public static final ItemStack BARRIER = new ItemStack(Items.BARRIER);
+ public static final ItemStack PLAYER = new ItemStack(Items.PLAYER_HEAD);
+ public static final ItemStack WATER = new ItemStack(Items.WATER_BUCKET);
+ public static final ItemStack LEATHER = new ItemStack(Items.LEATHER);
+ public static final ItemStack MITHRIL = new ItemStack(Items.PRISMARINE_CRYSTALS);
+ public static final ItemStack REDSTONE = new ItemStack(Items.REDSTONE);
+ public static final ItemStack FIRE = new ItemStack(Items.CAMPFIRE);
+ public static final ItemStack STRING = new ItemStack(Items.STRING);
+ public static final ItemStack WITHER = new ItemStack(Items.WITHER_SKELETON_SKULL);
+ public static final ItemStack FLESH = new ItemStack(Items.ROTTEN_FLESH);
+ public static final ItemStack DRAGON = new ItemStack(Items.DRAGON_HEAD);
+ public static final ItemStack DIAMOND = new ItemStack(Items.DIAMOND);
+ public static final ItemStack ICE = new ItemStack(Items.ICE);
+ public static final ItemStack CHEST = new ItemStack(Items.CHEST);
+ public static final ItemStack COMMAND = new ItemStack(Items.COMMAND_BLOCK);
+ public static final ItemStack SKULL = new ItemStack(Items.SKELETON_SKULL);
+ public static final ItemStack BOOK = new ItemStack(Items.WRITABLE_BOOK);
+ public static final ItemStack FURNACE = new ItemStack(Items.FURNACE);
+ public static final ItemStack CHESTPLATE = new ItemStack(Items.IRON_CHESTPLATE);
+ public static final ItemStack B_ROD = new ItemStack(Items.BLAZE_ROD);
+ public static final ItemStack BOW = new ItemStack(Items.BOW);
+ public static final ItemStack COPPER = new ItemStack(Items.COPPER_INGOT);
+ public static final ItemStack COMPOSTER = new ItemStack(Items.COMPOSTER);
+ public static final ItemStack SAPLING = new ItemStack(Items.OAK_SAPLING);
+ public static final ItemStack MILESTONE = new ItemStack(Items.LODESTONE);
+ public static final ItemStack PICKAXE = new ItemStack(Items.IRON_PICKAXE);
+ public static final ItemStack NETHER_STAR = new ItemStack(Items.NETHER_STAR);
+ public static final ItemStack HEART_OF_THE_SEA = new ItemStack(Items.HEART_OF_THE_SEA);
+ public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE);
+ public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE);
+ public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK);
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
new file mode 100644
index 00000000..ee4319dc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java
@@ -0,0 +1,160 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import me.xmrvizzy.skyblocker.mixin.PlayerListHudAccessor;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+
+/**
+ * This class may be used to get data from the player list. It doesn't get its
+ * data every frame, instead, a scheduler is used to update the data this class
+ * is holding periodically. The list is sorted like in the vanilla game.
+ */
+public class PlayerListMgr {
+
+ public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Regex");
+
+ private static List<PlayerListEntry> playerList;
+
+ public static void updateList() {
+
+ if (!Utils.isOnSkyblock()) {
+ return;
+ }
+
+ ClientPlayNetworkHandler cpnwh = MinecraftClient.getInstance().getNetworkHandler();
+
+ // check is needed, else game crash on server leave
+ if (cpnwh != null) {
+ playerList = cpnwh.getPlayerList().stream().sorted(PlayerListHudAccessor.getOrdering()).toList();
+ }
+ }
+
+ /**
+ * Get the display name at some index of the player list and apply a pattern to
+ * it
+ *
+ * @return the matcher if p fully matches, else null
+ */
+ public static Matcher regexAt(int idx, Pattern p) {
+
+ String str = PlayerListMgr.strAt(idx);
+
+ if (str == null) {
+ return null;
+ }
+
+ Matcher m = p.matcher(str);
+ if (!m.matches()) {
+ LOGGER.error("no match: \"{}\" against \"{}\"", str, p);
+ return null;
+ } else {
+ return m;
+ }
+ }
+
+ /**
+ * Get the display name at some index of the player list as string
+ *
+ * @return the string or null, if the display name is null, empty or whitespace
+ * only
+ */
+ public static String strAt(int idx) {
+
+ if (playerList == null) {
+ return null;
+ }
+
+ if (playerList.size() <= idx) {
+ return null;
+ }
+
+ Text txt = playerList.get(idx).getDisplayName();
+ if (txt == null) {
+ return null;
+ }
+ String str = txt.getString().trim();
+ if (str.length() == 0) {
+ return null;
+ }
+ return str;
+ }
+
+ /**
+ * Gets the display name at some index of the player list
+ *
+ * @return the text or null, if the display name is null
+ *
+ * @implNote currently designed specifically for crimson isles faction quests
+ * widget and the rift widgets, might not work correctly without
+ * modification for other stuff. you've been warned!
+ */
+ public static Text textAt(int idx) {
+
+ if (playerList == null) {
+ return null;
+ }
+
+ if (playerList.size() <= idx) {
+ return null;
+ }
+
+ Text txt = playerList.get(idx).getDisplayName();
+ if (txt == null) {
+ return null;
+ }
+
+ // Rebuild the text object to remove leading space thats in all faction quest
+ // stuff (also removes trailing space just in case)
+ MutableText newText = Text.empty();
+ int size = txt.getSiblings().size();
+
+ for (int i = 0; i < size; i++) {
+ Text current = txt.getSiblings().get(i);
+ String textToAppend = current.getString();
+
+ // Trim leading & trailing space - this can only be done at the start and end
+ // otherwise it'll produce malformed results
+ if (i == 0)
+ textToAppend = textToAppend.stripLeading();
+ if (i == size - 1)
+ textToAppend = textToAppend.stripTrailing();
+
+ newText.append(Text.literal(textToAppend).setStyle(current.getStyle()));
+ }
+
+ // Avoid returning an empty component - Rift advertisements needed this
+ if (newText.getString().length() == 0) {
+ return null;
+ }
+
+ return newText;
+ }
+
+ /**
+ * Get the display name at some index of the player list as Text as seen in the
+ * game
+ *
+ * @return the PlayerListEntry at that index
+ */
+ public static PlayerListEntry getRaw(int idx) {
+ return playerList.get(idx);
+ }
+
+ public static int getSize() {
+ return playerList.size();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java
new file mode 100644
index 00000000..c2f14d3c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java
@@ -0,0 +1,92 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.util;
+
+import me.xmrvizzy.skyblocker.utils.Utils;
+
+/**
+ * Uses data from the player list to determine the area the player is in.
+ */
+public class PlayerLocator {
+
+ public static enum Location {
+ DUNGEON,
+ GUEST_ISLAND,
+ HOME_ISLAND,
+ CRIMSON_ISLE,
+ DUNGEON_HUB,
+ FARMING_ISLAND,
+ PARK,
+ DWARVEN_MINES,
+ CRYSTAL_HOLLOWS,
+ END,
+ GOLD_MINE,
+ DEEP_CAVERNS,
+ HUB,
+ SPIDER_DEN,
+ JERRY,
+ GARDEN,
+ INSTANCED,
+ THE_RIFT,
+ UNKNOWN
+ }
+
+ public static Location getPlayerLocation() {
+
+ if (!Utils.isOnSkyblock()) {
+ return Location.UNKNOWN;
+ }
+
+ String areaDesciptor = PlayerListMgr.strAt(41);
+
+ if (areaDesciptor == null || areaDesciptor.length() < 6) {
+ return Location.UNKNOWN;
+ }
+
+ if (areaDesciptor.startsWith("Dungeon")) {
+ return Location.DUNGEON;
+ }
+
+ switch (areaDesciptor.substring(6)) {
+ case "Private Island":
+ String islandType = PlayerListMgr.strAt(44);
+ if (islandType == null) {
+ return Location.UNKNOWN;
+ } else if (islandType.endsWith("Guest")) {
+ return Location.GUEST_ISLAND;
+ } else {
+ return Location.HOME_ISLAND;
+ }
+ case "Crimson Isle":
+ return Location.CRIMSON_ISLE;
+ case "Dungeon Hub":
+ return Location.DUNGEON_HUB;
+ case "The Farming Islands":
+ return Location.FARMING_ISLAND;
+ case "The Park":
+ return Location.PARK;
+ case "Dwarven Mines":
+ return Location.DWARVEN_MINES;
+ case "Crystal Hollows":
+ return Location.CRYSTAL_HOLLOWS;
+ case "The End":
+ return Location.END;
+ case "Gold Mine":
+ return Location.GOLD_MINE;
+ case "Deep Caverns":
+ return Location.DEEP_CAVERNS;
+ case "Hub":
+ return Location.HUB;
+ case "Spider's Den":
+ return Location.SPIDER_DEN;
+ case "Jerry's Workshop":
+ return Location.JERRY;
+ case "Garden":
+ return Location.GARDEN;
+ case "Instanced":
+ return Location.INSTANCED;
+ case "The Rift":
+ return Location.THE_RIFT;
+ default:
+ return Location.UNKNOWN;
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
new file mode 100644
index 00000000..de90cf30
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java
@@ -0,0 +1,91 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud.Commission;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.math.MathHelper;
+
+// this widget shows the status of the king's commissions.
+// (dwarven mines and crystal hollows)
+
+public class CommsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ // match a comm
+ // group 1: comm name
+ // group 2: comm progress (without "%" for comms that show a percentage)
+ private static final Pattern COMM_PATTERN = Pattern.compile("(?<name>.*): (?<progress>.*)%?");
+
+ public CommsWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 50; i <= 53; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, COMM_PATTERN);
+ // end of comms found?
+ if (m == null) {
+ if (i == 50) {
+ this.addComponent(new IcoTextComponent());
+ }
+ break;
+ }
+
+ ProgressComponent pc;
+
+ String name = m.group("name");
+ String progress = m.group("progress");
+
+ if (progress.equals("DONE")) {
+ pc = new ProgressComponent(Ico.BOOK, Text.of(name), Text.of(progress), 100f, pcntToCol(100));
+ } else {
+ float pcnt = Float.parseFloat(progress.substring(0, progress.length() - 1));
+ pc = new ProgressComponent(Ico.BOOK, Text.of(name), pcnt, pcntToCol(pcnt));
+ }
+ this.addComponent(pc);
+ }
+ this.pack();
+ }
+
+ // for the dwarven hud
+ public CommsWidget(List<Commission> commissions, boolean isFancy) {
+ super(TITLE, Formatting.AQUA.getColorValue());
+ for (Commission comm : commissions) {
+
+ Text c = Text.literal(comm.commission());
+
+ float p = 100f;
+ if (!comm.progression().contains("DONE")) {
+ p = Float.parseFloat(comm.progression().substring(0, comm.progression().length() - 1));
+ }
+
+ Component comp;
+ if (isFancy) {
+ comp = new ProgressComponent(Ico.BOOK, c, p, pcntToCol(p));
+ } else {
+ comp = new PlainTextComponent(
+ Text.literal(comm.commission() + ": ")
+ .append(Text.literal(comm.progression()).formatted(Formatting.GREEN)));
+ }
+ this.addComponent(comp);
+ }
+ this.pack();
+
+ }
+
+ private int pcntToCol(float pcnt) {
+ return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
new file mode 100644
index 00000000..5922fcbc
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the garden's composter
+
+public class ComposterWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Composter").formatted(Formatting.GREEN,
+ Formatting.BOLD);
+
+ public ComposterWidget() {
+ super(TITLE, Formatting.GREEN.getColorValue());
+
+ this.addSimpleIcoText(Ico.SAPLING, "Organic Matter:", Formatting.YELLOW, 48);
+ this.addSimpleIcoText(Ico.FURNACE, "Fuel:", Formatting.BLUE, 49);
+ this.addSimpleIcoText(Ico.CLOCK, "Time Left:", Formatting.RED, 50);
+ this.addSimpleIcoText(Ico.COMPOSTER, "Stored Compost:", Formatting.DARK_GREEN, 51);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
new file mode 100644
index 00000000..48cb90bd
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java
@@ -0,0 +1,48 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about active super cookies
+// or not, if you're unwilling to buy one
+
+public class CookieWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Cookie Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ private static final Pattern COOKIE_PATTERN = Pattern.compile(".*\\nCookie Buff\\n(?<buff>.*)\\n");
+
+ public CookieWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ if (footertext == null || !footertext.contains("Cookie Buff")) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ Matcher m = COOKIE_PATTERN.matcher(footertext);
+ if (!m.find() || m.group("buff") == null) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ String buff = m.group("buff");
+ if (buff.startsWith("Not")) {
+ this.addComponent(new IcoTextComponent(Ico.COOKIE, Text.of("Not active")));
+ } else {
+ Text cookie = Text.literal("Time Left: ").append(buff);
+ this.addComponent(new IcoTextComponent(Ico.COOKIE, cookie));
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
new file mode 100644
index 00000000..6ad5268e
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of obtained dungeon buffs
+// TODO: could be more pretty, can't be arsed atm
+
+public class DungeonBuffWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Dungeon Buffs").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonBuffWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ if (footertext == null || !footertext.contains("Dungeon Buffs")) {
+ this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ String interesting = footertext.split("Dungeon Buffs")[1];
+ String[] lines = interesting.split("\n");
+
+ if (!lines[1].startsWith("Blessing")) {
+ this.addComponent(new PlainTextComponent(Text.literal("No buffs found!").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ for (int i = 1; i < lines.length; i++) {
+ if (lines[i].length() < 3) { // empty line is §s
+ break;
+ }
+ this.addComponent(new PlainTextComponent(Text.of(lines[i])));
+ }
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
new file mode 100644
index 00000000..78e1aeae
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java
@@ -0,0 +1,45 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows various dungeon info
+// deaths, healing, dmg taken, milestones
+
+public class DungeonDeathWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Death").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match the deaths entry
+ // group 1: amount of deaths
+ private static final Pattern DEATH_PATTERN = Pattern.compile("\\S*: \\((?<deathnum>\\d+)\\).*");
+
+ public DungeonDeathWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ Matcher m = PlayerListMgr.regexAt(25, DEATH_PATTERN);
+ if (m == null) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+ Formatting f = (m.group("deathnum").equals("0")) ? Formatting.GREEN : Formatting.RED;
+ Text d = Widget.simpleEntryText(m.group("deathnum"), "Deaths: ", f);
+ IcoTextComponent deaths = new IcoTextComponent(Ico.SKULL, d);
+ this.addComponent(deaths);
+ }
+
+ this.addSimpleIcoText(Ico.SWORD, "Damage Dealt:", Formatting.RED, 26);
+ this.addSimpleIcoText(Ico.POTION, "Healing Done:", Formatting.RED, 27);
+ this.addSimpleIcoText(Ico.NTAG, "Milestone:", Formatting.YELLOW, 28);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java
new file mode 100644
index 00000000..9bb250f7
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java
@@ -0,0 +1,42 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about... something?
+// related to downed people in dungeons, not sure what this is supposed to show
+
+public class DungeonDownedWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Downed").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonDownedWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ String down = PlayerListMgr.strAt(21);
+ if (down == null) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+
+ Formatting format = Formatting.RED;
+ if (down.endsWith("NONE")) {
+ format = Formatting.GRAY;
+ }
+ int idx = down.indexOf(": ");
+ Text downed = (idx == -1) ? null
+ : Widget.simpleEntryText(down.substring(idx + 2), "Downed: ", format);
+ IcoTextComponent d = new IcoTextComponent(Ico.SKULL, downed);
+ this.addComponent(d);
+ }
+
+ this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GRAY, 22);
+ this.addSimpleIcoText(Ico.POTION, "Revive:", Formatting.GRAY, 23);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
new file mode 100644
index 00000000..c1f9e235
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java
@@ -0,0 +1,99 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about a player in the current dungeon group
+
+public class DungeonPlayerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Player").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match a player entry
+ // group 1: name
+ // group 2: class (or literal "EMPTY" pre dungeon start)
+ // group 3: level (or nothing, if pre dungeon start)
+ // as a side effect, this regex keeps the iron man icon in the name
+ // not sure if that should be
+ private static final Pattern PLAYER_PATTERN = Pattern
+ .compile("\\[\\d*\\] (?<name>.*) \\((?<class>\\S*) ?(?<level>[LXVI]*)\\)");
+
+ private static final HashMap<String, ItemStack> ICOS = new HashMap<>();
+ private static final ArrayList<String> MSGS = new ArrayList<>();
+ static {
+ ICOS.put("Tank", Ico.CHESTPLATE);
+ ICOS.put("Mage", Ico.B_ROD);
+ ICOS.put("Berserk", Ico.DIASWORD);
+ ICOS.put("Archer", Ico.BOW);
+ ICOS.put("Healer", Ico.POTION);
+
+ MSGS.add("PRESS A TO JOIN");
+ MSGS.add("Invite a friend!");
+ MSGS.add("But nobody came.");
+ MSGS.add("More is better!");
+ }
+
+ // title needs to be changeable here
+ public DungeonPlayerWidget(int player) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ int start = 1 + (player - 1) * 4;
+
+ if (PlayerListMgr.strAt(start) == null) {
+ int idx = player - 2;
+ IcoTextComponent noplayer = new IcoTextComponent(Ico.SIGN,
+ Text.literal(MSGS.get(idx)).formatted(Formatting.GRAY));
+ this.addComponent(noplayer);
+ this.pack();
+ return;
+ }
+ Matcher m = PlayerListMgr.regexAt(start, PLAYER_PATTERN);
+ if (m == null) {
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ } else {
+
+ Text name = Text.literal("Name: ").append(Text.literal(m.group("name")).formatted(Formatting.YELLOW));
+ this.addComponent(new IcoTextComponent(Ico.PLAYER, name));
+
+ String cl = m.group("class");
+ String level = m.group("level");
+
+ if (level == null) {
+ PlainTextComponent ptc = new PlainTextComponent(
+ Text.literal("Player is dead").formatted(Formatting.RED));
+ this.addComponent(ptc);
+ } else {
+
+ Formatting clf = Formatting.GRAY;
+ ItemStack cli = Ico.BARRIER;
+ if (!cl.equals("EMPTY")) {
+ cli = ICOS.get(cl);
+ clf = Formatting.LIGHT_PURPLE;
+ cl += " " + m.group("level");
+ }
+
+ Text clazz = Text.literal("Class: ").append(Text.literal(cl).formatted(clf));
+ IcoTextComponent itclass = new IcoTextComponent(cli, clazz);
+ this.addComponent(itclass);
+ }
+ }
+
+ this.addSimpleIcoText(Ico.CLOCK, "Ult Cooldown:", Formatting.GOLD, start + 1);
+ this.addSimpleIcoText(Ico.POTION, "Revives:", Formatting.DARK_PURPLE, start + 2);
+
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
new file mode 100644
index 00000000..2529e876
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java
@@ -0,0 +1,55 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about all puzzeles in the dungeon (name and status)
+
+public class DungeonPuzzleWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Puzzles").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match a puzzle entry
+ // group 1: name
+ // group 2: status
+ // " ?.*" to diescard the solver's name if present
+ // the teleport maze has a trailing whitespace that messes with the regex
+ private static final Pattern PUZZLE_PATTERN = Pattern.compile("(?<name>.*): \\[(?<status>.*)\\] ?.*");
+
+ public DungeonPuzzleWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ int pos = 48;
+
+ while (pos < 60) {
+ Matcher m = PlayerListMgr.regexAt(pos, PUZZLE_PATTERN);
+ if (m == null) {
+ break;
+ }
+ Text t = Text.literal(m.group("name") + ": ")
+ .append(Text.literal("[").formatted(Formatting.GRAY))
+ .append(m.group("status"))
+ .append(Text.literal("]").formatted(Formatting.GRAY));
+ IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, t);
+ this.addComponent(itc);
+ pos++;
+ // code points for puzzle status chars unsolved and solved: 10022, 10004
+ // not sure which one is which
+ // still need to find out codepoint for the puzzle failed char
+ }
+ if (pos == 48) {
+ this.addComponent(
+ new IcoTextComponent(Ico.BARRIER, Text.literal("No puzzles!").formatted(Formatting.GRAY)));
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
new file mode 100644
index 00000000..93eb69de
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the secrets of the dungeon
+
+public class DungeonSecretWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public DungeonSecretWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31);
+ this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
new file mode 100644
index 00000000..81b8f907
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java
@@ -0,0 +1,47 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows broad info about the current dungeon
+// opened/completed rooms, % of secrets found and time taken
+
+public class DungeonServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Dungeon Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // match the secrets text
+ // group 1: % of secrets found (without "%")
+ private static final Pattern SECRET_PATTERN = Pattern.compile("Secrets Found: (?<secnum>.*)%");
+
+ public DungeonServerWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.AQUA, 41);
+ this.addSimpleIcoText(Ico.SIGN, "Rooms Visited:", Formatting.DARK_PURPLE, 42);
+ this.addSimpleIcoText(Ico.SIGN, "Rooms Completed:", Formatting.LIGHT_PURPLE, 43);
+
+ Matcher m = PlayerListMgr.regexAt(44, SECRET_PATTERN);
+ if (m == null) {
+ this.addComponent(new ProgressComponent());
+ } else {
+ ProgressComponent scp = new ProgressComponent(Ico.CHEST, Text.of("Secrets found:"),
+ Float.parseFloat(m.group("secnum")),
+ Formatting.DARK_PURPLE.getColorValue());
+ this.addComponent(scp);
+ }
+
+ this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GOLD, 45);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
new file mode 100644
index 00000000..cd39a25a
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java
@@ -0,0 +1,64 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widgte shows, how many active effects you have.
+// it also shows one of those in detail.
+// the parsing is super suspect and should be replaced by some regexes sometime later
+
+public class EffectWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Effect Info").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ public EffectWidget(String footertext) {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+
+ if (footertext == null || !footertext.contains("Active Effects")) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+
+ }
+
+ String[] lines = footertext.split("Active Effects")[1].split("\n");
+ if (lines.length < 2) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ if (lines[1].startsWith("No")) {
+ Text txt = Text.literal("No effects active").formatted(Formatting.GRAY);
+ this.addComponent(new IcoTextComponent(Ico.POTION, txt));
+ } else if (lines[1].contains("God")) {
+ String timeleft = lines[1].split("! ")[1];
+ Text godpot = Text.literal("God potion!").formatted(Formatting.RED);
+ Text txttleft = Text.literal(timeleft).formatted(Formatting.LIGHT_PURPLE);
+ IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, godpot, txttleft);
+ this.addComponent(iftc);
+ } else {
+ String number = lines[1].substring("You have ".length());
+ int idx = number.indexOf(' ');
+ if (idx == -1 || lines.length < 4) {
+ this.addComponent(new IcoFatTextComponent());
+ this.pack();
+ return;
+ }
+ number = number.substring(0, idx);
+ Text active = Text.literal("Active Effects: ")
+ .append(Text.literal(number).formatted(Formatting.YELLOW));
+
+ IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, active,
+ Text.literal(lines[3]).formatted(Formatting.AQUA));
+ this.addComponent(iftc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
new file mode 100644
index 00000000..ed07982c
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java
@@ -0,0 +1,103 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows the status or results of the current election
+
+public class ElectionWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Election Info").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ private static final HashMap<String, ItemStack> MAYOR_DATA = new HashMap<>();
+
+ private static final Text EL_OVER = Text.literal("Election ")
+ .append(Text.literal("over!").formatted(Formatting.RED));
+
+ // pattern matching a candidate while people are voting
+ // group 1: name
+ // group 2: % of votes
+ private static final Pattern VOTE_PATTERN = Pattern.compile("(?<mayor>\\S*): \\|+ \\((?<pcnt>\\d*)%\\)");
+
+ static {
+ MAYOR_DATA.put("Aatrox", Ico.DIASWORD);
+ MAYOR_DATA.put("Cole", Ico.PICKAXE);
+ MAYOR_DATA.put("Diana", Ico.BONE);
+ MAYOR_DATA.put("Diaz", Ico.GOLD);
+ MAYOR_DATA.put("Finnegan", Ico.HOE);
+ MAYOR_DATA.put("Foxy", Ico.SUGAR);
+ MAYOR_DATA.put("Paul", Ico.COMPASS);
+ MAYOR_DATA.put("Scorpius", Ico.MOREGOLD);
+ MAYOR_DATA.put("Jerry", Ico.VILLAGER);
+ MAYOR_DATA.put("Derpy", Ico.DBUSH);
+ MAYOR_DATA.put("Marina", Ico.FISH_ROD);
+ }
+
+ private static final Formatting[] COLS = { Formatting.GOLD, Formatting.RED, Formatting.LIGHT_PURPLE };
+
+ public ElectionWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ String status = PlayerListMgr.strAt(76);
+ if (status == null) {
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ if (status.contains("Over!")) {
+ // election is over
+ IcoTextComponent over = new IcoTextComponent(Ico.BARRIER, EL_OVER);
+ this.addComponent(over);
+
+ String win = PlayerListMgr.strAt(77);
+ if (win == null || !win.contains(": ")) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+ String winnername = win.split(": ")[1];
+ Text winnertext = Widget.simpleEntryText(winnername, "Winner: ", Formatting.GREEN);
+ IcoTextComponent winner = new IcoTextComponent(MAYOR_DATA.get(winnername), winnertext);
+ this.addComponent(winner);
+ }
+
+ this.addSimpleIcoText(Ico.PLAYER, "Participants:", Formatting.AQUA, 78);
+ this.addSimpleIcoText(Ico.SIGN, "Year:", Formatting.LIGHT_PURPLE, 79);
+
+ } else {
+ // election is going on
+ this.addSimpleIcoText(Ico.CLOCK, "End in:", Formatting.GOLD, 76);
+
+ for (int i = 77; i <= 79; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, VOTE_PATTERN);
+ if (m == null) {
+ this.addComponent(new ProgressComponent());
+ } else {
+
+ String mayorname = m.group("mayor");
+ String pcntstr = m.group("pcnt");
+ float pcnt = Float.parseFloat(pcntstr);
+ Text candidate = Text.literal(mayorname).formatted(COLS[i - 77]);
+ ProgressComponent pc = new ProgressComponent(MAYOR_DATA.get(mayorname), candidate, pcnt,
+ COLS[i - 77].getColorValue());
+ this.addComponent(pc);
+ }
+ }
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java
new file mode 100644
index 00000000..52d6cfbd
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EmptyWidget.java
@@ -0,0 +1,24 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// empty widget for when nothing can be shown
+
+public class EmptyWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Empty").formatted(Formatting.RED,
+ Formatting.BOLD);
+
+ public EmptyWidget() {
+ super(TITLE, Formatting.RED.getColorValue());
+
+ Text info = Text.of("No info for this area!");
+ PlainTextComponent inf = new PlainTextComponent(info);
+ this.addComponent(inf);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
new file mode 100644
index 00000000..fc0780e1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows your dungeon essences (dungeon hub only)
+
+public class EssenceWidget extends Widget {
+
+ private Text undead, wither, diamond, gold, dragon, spider, ice, crimson;
+
+ private static final MutableText TITLE = Text.literal("Essences").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public EssenceWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ wither = Widget.simpleEntryText(46, "Wither:", Formatting.DARK_PURPLE);
+ spider = Widget.simpleEntryText(47, "Spider:", Formatting.DARK_PURPLE);
+ undead = Widget.simpleEntryText(48, "Undead:", Formatting.DARK_PURPLE);
+ dragon = Widget.simpleEntryText(49, "Dragon:", Formatting.DARK_PURPLE);
+ gold = Widget.simpleEntryText(50, "Gold:", Formatting.DARK_PURPLE);
+ diamond = Widget.simpleEntryText(51, "Diamond:", Formatting.DARK_PURPLE);
+ ice = Widget.simpleEntryText(52, "Ice:", Formatting.DARK_PURPLE);
+ crimson = Widget.simpleEntryText(53, "Crimson:", Formatting.DARK_PURPLE);
+
+ TableComponent tc = new TableComponent(2, 4, Formatting.DARK_AQUA.getColorValue());
+
+ tc.addToCell(0, 0, new IcoTextComponent(Ico.WITHER, wither));
+ tc.addToCell(0, 1, new IcoTextComponent(Ico.STRING, spider));
+ tc.addToCell(0, 2, new IcoTextComponent(Ico.FLESH, undead));
+ tc.addToCell(0, 3, new IcoTextComponent(Ico.DRAGON, dragon));
+ tc.addToCell(1, 0, new IcoTextComponent(Ico.GOLD, gold));
+ tc.addToCell(1, 1, new IcoTextComponent(Ico.DIAMOND, diamond));
+ tc.addToCell(1, 2, new IcoTextComponent(Ico.ICE, ice));
+ tc.addToCell(1, 3, new IcoTextComponent(Ico.REDSTONE, crimson));
+ this.addComponent(tc);
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
new file mode 100644
index 00000000..1b46e621
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about ongoing events (e.g. election)
+
+public class EventWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD);
+
+ public EventWidget(boolean isInGarden) {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ // hypixel devs carefully inserting the most random edge cases #317:
+ // the event info is placed a bit differently when in the garden.
+ int offset = (isInGarden) ? -1 : 0;
+
+ this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.YELLOW, 73 + offset);
+
+ // this could look better
+ Text time = Widget.plainEntryText(74 + offset);
+ IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time);
+ this.addComponent(t);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
new file mode 100644
index 00000000..ddf51f32
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java
@@ -0,0 +1,68 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about fire sales when in the hub.
+// or not, if there isn't one going on
+
+public class FireSaleWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Fire Sale").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ // matches a fire sale item
+ // group 1: item name
+ // group 2: # items available
+ // group 3: # items available in total (1 digit + "k")
+ private static final Pattern FIRE_PATTERN = Pattern.compile("(?<item>.*): (?<avail>\\d*)/(?<total>[0-9.]*)k");
+
+ public FireSaleWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ String event = PlayerListMgr.strAt(46);
+
+ if (event == null) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ if (event.contains("Starts In")) {
+ this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.DARK_AQUA, 46);
+ this.pack();
+ return;
+ }
+
+ for (int i = 46;; i++) {
+ Matcher m = PlayerListMgr.regexAt( i, FIRE_PATTERN);
+ if (m == null) {
+ break;
+ }
+ String avail = m.group("avail");
+ Text itemTxt = Text.literal(m.group("item"));
+ float total = Float.parseFloat(m.group("total")) * 1000;
+ Text prgressTxt = Text.literal(String.format("%s/%.0f", avail, total));
+ float pcnt = (Float.parseFloat(avail) / (total)) * 100f;
+ ProgressComponent pc = new ProgressComponent(Ico.GOLD, itemTxt, prgressTxt, pcnt, pcntToCol(pcnt));
+ this.addComponent(pc);
+ }
+ this.pack();
+
+ }
+
+ private int pcntToCol(float pcnt) {
+ return MathHelper.hsvToRgb( pcnt / 300f, 0.9f, 0.9f);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
new file mode 100644
index 00000000..da1ba6c5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java
@@ -0,0 +1,79 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows what you're forging right now.
+// for locked slots, the unlock requirement is shown
+
+public class ForgeWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Forge Status").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ForgeWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ int forgestart = 54;
+ // why is it forges and not looms >:(
+ String pos = PlayerListMgr.strAt(53);
+ if (pos == null) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ if (!pos.startsWith("Forges")) {
+ forgestart += 2;
+ }
+
+ for (int i = forgestart, slot = 1; i < forgestart + 5 && i < 60; i++, slot++) {
+ String fstr = PlayerListMgr.strAt(i);
+ if (fstr == null || fstr.length() < 3) {
+ if (i == forgestart) {
+ this.addComponent(new IcoTextComponent());
+ }
+ break;
+ }
+ Component c;
+ Text l1, l2;
+
+ switch (fstr.substring(3)) {
+ case "LOCKED":
+ l1 = Text.literal("Locked").formatted(Formatting.RED);
+ l2 = switch (slot) {
+ case 3 -> Text.literal("Needs HotM 3").formatted(Formatting.GRAY);
+ case 4 -> Text.literal("Needs HotM 4").formatted(Formatting.GRAY);
+ case 5 -> Text.literal("Needs PotM 2").formatted(Formatting.GRAY);
+ default ->
+ Text.literal("This message should not appear").formatted(Formatting.RED, Formatting.BOLD);
+ };
+ c = new IcoFatTextComponent(Ico.BARRIER, l1, l2);
+ break;
+ case "EMPTY":
+ l1 = Text.literal("Empty").formatted(Formatting.GRAY);
+ c = new IcoTextComponent(Ico.FURNACE, l1);
+ break;
+ default:
+ String[] parts = fstr.split(": ");
+ if (parts.length != 2) {
+ c = new IcoFatTextComponent();
+ } else {
+ l1 = Text.literal(parts[0].substring(3)).formatted(Formatting.YELLOW);
+ l2 = Text.literal("Done in: ").formatted(Formatting.GRAY).append(Text.literal(parts[1]).formatted(Formatting.WHITE));
+ c = new IcoFatTextComponent(Ico.FIRE, l1, l2);
+ }
+ break;
+ }
+ this.addComponent(c);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
new file mode 100644
index 00000000..b0fc160f
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java
@@ -0,0 +1,53 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the garden server
+
+public class GardenServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ // match the next visitor in the garden
+ // group 1: visitor name
+ private static final Pattern VISITOR_PATTERN = Pattern.compile("Next Visitor: (?<vis>.*)");
+
+ public GardenServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+ this.addSimpleIcoText(Ico.COPPER, "Copper:", Formatting.GOLD, 44);
+
+ Matcher m = PlayerListMgr.regexAt(45, VISITOR_PATTERN);
+ if (m == null ) {
+ this.addComponent(new IcoTextComponent());
+ this.pack();
+ return;
+ }
+
+ String vis = m.group("vis");
+ Formatting col;
+ if (vis.equals("Not Unlocked!")) {
+ col = Formatting.RED;
+ } else {
+ col = Formatting.GREEN;
+ }
+ Text visitor = Widget.simpleEntryText(vis, "Next Visitor: ", col);
+ IcoTextComponent v = new IcoTextComponent(Ico.PLAYER, visitor);
+ this.addComponent(v);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java
new file mode 100644
index 00000000..26e29ce2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java
@@ -0,0 +1,78 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about your skills while in the garden
+
+public class GardenSkillsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ // match the skill entry
+ // group 1: skill name and level
+ // group 2: progress to next level (without "%")
+ private static final Pattern SKILL_PATTERN = Pattern
+ .compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%");
+ // same, but with leading space
+ private static final Pattern MS_PATTERN = Pattern.compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%");
+
+ public GardenSkillsWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ ProgressComponent pc;
+ Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN);
+ if (m == null) {
+ pc = new ProgressComponent();
+ } else {
+
+ String strpcnt = m.group("progress");
+ String skill = m.group("skill");
+
+ float pcnt = Float.parseFloat(strpcnt);
+ pc = new ProgressComponent(Ico.LANTERN, Text.of(skill), pcnt,
+ Formatting.GOLD.getColorValue());
+ }
+
+ this.addComponent(pc);
+
+ Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE);
+ IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed);
+ Text farmfort = Widget.simpleEntryText(68, "FFO", Formatting.GOLD);
+ IcoTextComponent ffo = new IcoTextComponent(Ico.HOE, farmfort);
+
+ TableComponent tc = new TableComponent(2, 1, Formatting.YELLOW.getColorValue());
+ tc.addToCell(0, 0, spd);
+ tc.addToCell(1, 0, ffo);
+ this.addComponent(tc);
+
+ ProgressComponent pc2;
+ m = PlayerListMgr.regexAt(69, MS_PATTERN);
+ if (m == null) {
+ pc2 = new ProgressComponent();
+ } else {
+ String strpcnt = m.group("progress");
+ String skill = m.group("skill");
+
+ float pcnt = Float.parseFloat(strpcnt);
+ pc2 = new ProgressComponent(Ico.MILESTONE, Text.of(skill), pcnt,
+ Formatting.GREEN.getColorValue());
+
+ }
+ this.addComponent(pc2);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
new file mode 100644
index 00000000..cb208e92
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the private island you're visiting
+
+public class GuestServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public GuestServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.SIGN, "Owner:", Formatting.GREEN, 43);
+ this.addSimpleIcoText(Ico.SIGN, "Status:", Formatting.BLUE, 44);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java
new file mode 100644
index 00000000..e0f5f1a3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java
@@ -0,0 +1,44 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of all people visiting the same private island as you
+
+public class IslandGuestsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Guests").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ // matches a player entry, removing their level and the hand icon
+ // group 1: player name
+ private static final Pattern GUEST_PATTERN = Pattern.compile("\\[\\d*\\] (.*) \\[.\\]");
+
+ public IslandGuestsWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+ for (int i = 21; i < 40; i++) {
+ String str = PlayerListMgr.strAt(i);
+ if (str == null) {
+ if (i == 21) {
+ this.addComponent(new PlainTextComponent(Text.literal("No Visitors!").formatted(Formatting.GRAY)));
+ }
+ break;
+ }
+ Matcher m = PlayerListMgr.regexAt( i, GUEST_PATTERN);
+ if (m == null) {
+ this.addComponent(new PlainTextComponent(Text.of("???")));
+ } else {
+ this.addComponent(new PlainTextComponent(Text.of(m.group(1))));
+ }
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
new file mode 100644
index 00000000..6c2d6b47
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of the owners of a home island while guesting
+
+public class IslandOwnersWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // matches an owner
+ // group 1: player name
+ // group 2: last seen, if owner not online
+ private static final Pattern OWNER_PATTERN = Pattern
+ .compile("^(?<nameA>.*) \\((?<lastseen>.*)\\)$|^\\[\\d*\\] (?<nameB>.*)$|^(?<nameC>.*)$");
+
+ public IslandOwnersWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ for (int i = 1; i < 20; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN);
+ if (m == null) {
+ break;
+ }
+
+ String name = null, lastseen = null;
+ Formatting format = null;
+ if (m.group("nameA") != null) {
+ name = m.group("nameA");
+ lastseen = m.group("lastseen");
+ format = Formatting.GRAY;
+ } else if (m.group("nameB")!=null){
+ name = m.group("nameB");
+ lastseen = "Online";
+ format = Formatting.WHITE;
+ } else {
+ name = m.group("nameC");
+ lastseen = "Online";
+ format = Formatting.WHITE;
+ }
+
+ Text entry = Text.literal(name)
+ .append(
+ Text.literal(" (" + lastseen + ")")
+ .formatted(format));
+ PlainTextComponent ptc = new PlainTextComponent(entry);
+ this.addComponent(ptc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
new file mode 100644
index 00000000..4324dad9
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java
@@ -0,0 +1,37 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of the owners while on your home island
+
+public class IslandSelfWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE,
+ Formatting.BOLD);
+
+ // matches an owner
+ // group 1: player name, optionally offline time
+ private static final Pattern OWNER_PATTERN = Pattern.compile("^\\[\\d*\\] (.*)$|^(.*)$");
+
+ public IslandSelfWidget() {
+ super(TITLE, Formatting.DARK_PURPLE.getColorValue());
+ for (int i = 1; i < 20; i++) {
+ Matcher m = PlayerListMgr.regexAt( i, OWNER_PATTERN);
+ if (m == null) {
+ break;
+ }
+ PlainTextComponent ptc = new PlainTextComponent(Text.of(m.group(1)));
+ this.addComponent(ptc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
new file mode 100644
index 00000000..2b02c514
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about your home island
+
+public class IslandServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public IslandServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Crystals:", Formatting.DARK_PURPLE, 43);
+ this.addSimpleIcoText(Ico.CHEST, "Stash:", Formatting.GREEN, 44);
+ this.addSimpleIcoText(Ico.COMMAND, "Minions:", Formatting.BLUE, 45);
+
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
new file mode 100644
index 00000000..8d49efaa
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java
@@ -0,0 +1,60 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the current jacob's contest (garden only)
+
+public class JacobsContestWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Jacob's Contest").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ private static final HashMap<String, ItemStack> FARM_DATA = new HashMap<>();
+
+ // again, there HAS to be a better way to do this
+ static {
+ FARM_DATA.put("Wheat", new ItemStack(Items.WHEAT));
+ FARM_DATA.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE));
+ FARM_DATA.put("Carrot", new ItemStack(Items.CARROT));
+ FARM_DATA.put("Potato", new ItemStack(Items.POTATO));
+ FARM_DATA.put("Melon", new ItemStack(Items.MELON_SLICE));
+ FARM_DATA.put("Pumpkin", new ItemStack(Items.PUMPKIN));
+ FARM_DATA.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS));
+ FARM_DATA.put("Nether Wart", new ItemStack(Items.NETHER_WART));
+ FARM_DATA.put("Cactus", new ItemStack(Items.CACTUS));
+ FARM_DATA.put("Mushroom", new ItemStack(Items.RED_MUSHROOM));
+ }
+
+ public JacobsContestWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.GOLD, 76);
+
+ TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue());
+
+ for (int i = 77; i < 80; i++) {
+ String item = PlayerListMgr.strAt(i);
+ IcoTextComponent itc;
+ if (item == null) {
+ itc = new IcoTextComponent();
+ } else {
+ itc = new IcoTextComponent(FARM_DATA.get(item), Text.of(item));
+ }
+ tc.addToCell(0, i - 77, itc);
+ }
+ this.addComponent(tc);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
new file mode 100644
index 00000000..fe52fcdf
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java
@@ -0,0 +1,134 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about minions placed on the home island
+
+public class MinionWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Minions").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ private static final HashMap<String, ItemStack> MIN_ICOS = new HashMap<>();
+
+ // hmm...
+ static {
+ MIN_ICOS.put("Blaze", new ItemStack(Items.BLAZE_ROD));
+ MIN_ICOS.put("Cave Spider", new ItemStack(Items.SPIDER_EYE));
+ MIN_ICOS.put("Creeper", new ItemStack(Items.GUNPOWDER));
+ MIN_ICOS.put("Enderman", new ItemStack(Items.ENDER_PEARL));
+ MIN_ICOS.put("Ghast", new ItemStack(Items.GHAST_TEAR));
+ MIN_ICOS.put("Magma Cube", new ItemStack(Items.MAGMA_CREAM));
+ MIN_ICOS.put("Skeleton", new ItemStack(Items.BONE));
+ MIN_ICOS.put("Slime", new ItemStack(Items.SLIME_BALL));
+ MIN_ICOS.put("Spider", new ItemStack(Items.STRING));
+ MIN_ICOS.put("Zombie", new ItemStack(Items.ROTTEN_FLESH));
+ MIN_ICOS.put("Cactus", new ItemStack(Items.CACTUS));
+ MIN_ICOS.put("Carrot", new ItemStack(Items.CARROT));
+ MIN_ICOS.put("Chicken", new ItemStack(Items.CHICKEN));
+ MIN_ICOS.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS));
+ MIN_ICOS.put("Cow", new ItemStack(Items.BEEF));
+ MIN_ICOS.put("Melon", new ItemStack(Items.MELON_SLICE));
+ MIN_ICOS.put("Mushroom", new ItemStack(Items.RED_MUSHROOM));
+ MIN_ICOS.put("Nether Wart", new ItemStack(Items.NETHER_WART));
+ MIN_ICOS.put("Pig", new ItemStack(Items.PORKCHOP));
+ MIN_ICOS.put("Potato", new ItemStack(Items.POTATO));
+ MIN_ICOS.put("Pumpkin", new ItemStack(Items.PUMPKIN));
+ MIN_ICOS.put("Rabbit", new ItemStack(Items.RABBIT));
+ MIN_ICOS.put("Sheep", new ItemStack(Items.WHITE_WOOL));
+ MIN_ICOS.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE));
+ MIN_ICOS.put("Wheat", new ItemStack(Items.WHEAT));
+ MIN_ICOS.put("Clay", new ItemStack(Items.CLAY));
+ MIN_ICOS.put("Fishing", new ItemStack(Items.FISHING_ROD));
+ MIN_ICOS.put("Coal", new ItemStack(Items.COAL));
+ MIN_ICOS.put("Cobblestone", new ItemStack(Items.COBBLESTONE));
+ MIN_ICOS.put("Diamond", new ItemStack(Items.DIAMOND));
+ MIN_ICOS.put("Emerald", new ItemStack(Items.EMERALD));
+ MIN_ICOS.put("End Stone", new ItemStack(Items.END_STONE));
+ MIN_ICOS.put("Glowstone", new ItemStack(Items.GLOWSTONE_DUST));
+ MIN_ICOS.put("Gold", new ItemStack(Items.GOLD_INGOT));
+ MIN_ICOS.put("Gravel", new ItemStack(Items.GRAVEL));
+ MIN_ICOS.put("Hard Stone", new ItemStack(Items.STONE));
+ MIN_ICOS.put("Ice", new ItemStack(Items.ICE));
+ MIN_ICOS.put("Iron", new ItemStack(Items.IRON_INGOT));
+ MIN_ICOS.put("Lapis", new ItemStack(Items.LAPIS_LAZULI));
+ MIN_ICOS.put("Mithril", new ItemStack(Items.PRISMARINE_CRYSTALS));
+ MIN_ICOS.put("Mycelium", new ItemStack(Items.MYCELIUM));
+ MIN_ICOS.put("Obsidian", new ItemStack(Items.OBSIDIAN));
+ MIN_ICOS.put("Quartz", new ItemStack(Items.QUARTZ));
+ MIN_ICOS.put("Red Sand", new ItemStack(Items.RED_SAND));
+ MIN_ICOS.put("Redstone", new ItemStack(Items.REDSTONE));
+ MIN_ICOS.put("Sand", new ItemStack(Items.SAND));
+ MIN_ICOS.put("Snow", new ItemStack(Items.SNOWBALL));
+ MIN_ICOS.put("Inferno", new ItemStack(Items.BLAZE_SPAWN_EGG));
+ MIN_ICOS.put("Revenant", new ItemStack(Items.ZOMBIE_SPAWN_EGG));
+ MIN_ICOS.put("Tarantula", new ItemStack(Items.SPIDER_SPAWN_EGG));
+ MIN_ICOS.put("Vampire", new ItemStack(Items.REDSTONE));
+ MIN_ICOS.put("Voidling", new ItemStack(Items.ENDERMAN_SPAWN_EGG));
+ MIN_ICOS.put("Acacia", new ItemStack(Items.ACACIA_LOG));
+ MIN_ICOS.put("Birch", new ItemStack(Items.BIRCH_LOG));
+ MIN_ICOS.put("Dark Oak", new ItemStack(Items.DARK_OAK_LOG));
+ MIN_ICOS.put("Flower", new ItemStack(Items.POPPY));
+ MIN_ICOS.put("Jungle", new ItemStack(Items.JUNGLE_LOG));
+ MIN_ICOS.put("Oak", new ItemStack(Items.OAK_LOG));
+ MIN_ICOS.put("Spruce", new ItemStack(Items.SPRUCE_LOG));
+ }
+
+ // matches a minion entry
+ // group 1: name
+ // group 2: level
+ // group 3: status
+ public static final Pattern MINION_PATTERN = Pattern.compile("(?<name>.*) (?<level>[XVI]*) \\[(?<status>.*)\\]");
+
+ public MinionWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 48; i < 59; i++) {
+ Matcher m = PlayerListMgr.regexAt(i, MINION_PATTERN);
+ if (m != null) {
+
+ String min = m.group("name");
+ String lvl = m.group("level");
+ String stat = m.group("status");
+
+ MutableText mt = Text.literal(min + " " + lvl).append(Text.literal(": "));
+
+ Formatting format = Formatting.RED;
+ if (stat.equals("ACTIVE")) {
+ format = Formatting.GREEN;
+ } else if (stat.equals("SLOW")) {
+ format = Formatting.YELLOW;
+ }
+ // makes "BLOCKED" also red. in reality, it's some kind of crimson
+ mt.append(Text.literal(stat).formatted(format));
+
+ IcoTextComponent itc = new IcoTextComponent(MIN_ICOS.get(min), mt);
+ this.addComponent(itc);
+
+ } else {
+ break;
+ }
+ }
+
+ // if more minions are placed than the tab menu can display,
+ // a "And X more..." text is shown
+ // look for that and add it to the widget
+ String more = PlayerListMgr.strAt(59);
+ if (more != null) {
+ this.addComponent(new PlainTextComponent(Text.of(more)));
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
new file mode 100644
index 00000000..4148ed77
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about the park server
+
+public class ParkServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ParkServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+ this.addSimpleIcoText(Ico.WATER, "Rain:", Formatting.BLUE, 44);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
new file mode 100644
index 00000000..2cd710eb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java
@@ -0,0 +1,73 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlayerComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows a list of players with their skins.
+// responsible for non-private-island areas
+
+public class PlayerListWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Players").formatted(Formatting.GREEN,
+ Formatting.BOLD);
+
+ private ArrayList<PlayerListEntry> list = new ArrayList<>();
+
+ public PlayerListWidget() {
+ super(TITLE, Formatting.GREEN.getColorValue());
+
+ // hard cap to 4x20 entries.
+ // 5x20 is too wide (and not possible in theory. in reality however...)
+ int listlen = Math.min(PlayerListMgr.getSize(), 160);
+
+ // list isn't fully loaded, so our hack won't work...
+ if (listlen < 80) {
+ this.addComponent(new PlainTextComponent(Text.literal("List loading...").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ // unintuitive int ceil division stolen from
+ // https://stackoverflow.com/questions/7139382/java-rounding-up-to-an-int-using-math-ceil#21830188
+ int tblW = ((listlen - 80) - 1) / 20 + 1;
+
+ TableComponent tc = new TableComponent(tblW, (listlen - 80 >= 20) ? 20 : listlen - 80,
+ Formatting.GREEN.getColorValue());
+
+ for (int i = 80; i < listlen; i++) {
+ list.add(PlayerListMgr.getRaw(i));
+ }
+
+ Collections.sort(list, new Comparator<PlayerListEntry>() {
+ @Override
+ public int compare(PlayerListEntry o1, PlayerListEntry o2) {
+ return o1.getProfile().getName().toLowerCase().compareTo(o2.getProfile().getName().toLowerCase());
+ }
+ });
+
+ int x = 0, y = 0;
+
+ for (PlayerListEntry ple : list) {
+ tc.addToCell(x, y, new PlayerComponent(ple));
+ y++;
+ if (y >= 20) {
+ y = 0;
+ x++;
+ }
+ }
+
+ this.addComponent(tc);
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
new file mode 100644
index 00000000..459e3de2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows how much mithril and gemstone powder you have
+// (dwarven mines and crystal hollows)
+
+public class PowderWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Powders").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public PowderWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MITHRIL, "Mithril:", Formatting.AQUA, 46);
+ this.addSimpleIcoText(Ico.EMERALD, "Gemstone:", Formatting.DARK_PURPLE, 47);
+
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
new file mode 100644
index 00000000..a6d9e82d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java
@@ -0,0 +1,25 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about your profile and bank
+
+public class ProfileWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.YELLOW, Formatting.BOLD);
+
+ public ProfileWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61);
+ this.addSimpleIcoText(Ico.BONE, "Pet Sitter:", Formatting.AQUA, 62);
+ this.addSimpleIcoText(Ico.EMERALD, "Balance:", Formatting.GOLD, 63);
+ this.addSimpleIcoText(Ico.CLOCK, "Interest in:", Formatting.GOLD, 64);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java
new file mode 100644
index 00000000..43b741ba
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows your crimson isle faction quests
+
+public class QuestWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Faction Quests").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ public QuestWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+
+ for (int i = 51; i < 56; i++) {
+ Text q = PlayerListMgr.textAt(i);
+ IcoTextComponent itc = new IcoTextComponent(Ico.BOOK, q);
+ this.addComponent(itc);
+ }
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java
new file mode 100644
index 00000000..3685e0ca
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java
@@ -0,0 +1,68 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows your faction status (crimson isle)
+
+public class ReputationWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Faction Status").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ // matches your faction alignment progress
+ // group 1: percentage to next alignment level
+ private static final Pattern PROGRESS_PATTERN = Pattern.compile("\\|+ \\((?<prog>[0-9.]*)%\\)");
+
+ // matches alignment level names
+ // group 1: left level name
+ // group 2: right level name
+ private static final Pattern STATE_PATTERN = Pattern.compile("(?<from>\\S*) *(?<to>\\S*)");
+
+ public ReputationWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+
+ String fracstr = PlayerListMgr.strAt(45);
+
+ int spaceidx;
+ IcoTextComponent faction;
+ if (fracstr == null || (spaceidx = fracstr.indexOf(' ')) == -1) {
+ faction = new IcoTextComponent();
+ } else {
+ String fname = fracstr.substring(0, spaceidx);
+ if (fname.equals("Mage")) {
+ faction = new IcoTextComponent(Ico.POTION, Text.literal(fname).formatted(Formatting.DARK_AQUA));
+ } else {
+ faction = new IcoTextComponent(Ico.SWORD, Text.literal(fname).formatted(Formatting.RED));
+ }
+ }
+ this.addComponent(faction);
+
+ Text rep = Widget.plainEntryText(46);
+ Matcher prog = PlayerListMgr.regexAt(47, PROGRESS_PATTERN);
+ Matcher state = PlayerListMgr.regexAt(48, STATE_PATTERN);
+
+ if (prog == null || state == null) {
+ this.addComponent(new ProgressComponent());
+ } else {
+ float pcnt = Float.parseFloat(prog.group("prog"));
+ Text reputationText = state.group("from").equals("Max") ? Text.literal("Max Reputation") : Text.literal(state.group("from") + " -> " + state.group("to"));
+ ProgressComponent pc = new ProgressComponent(Ico.LANTERN,
+ reputationText, rep, pcnt,
+ Formatting.AQUA.getColorValue());
+ this.addComponent(pc);
+ }
+
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
new file mode 100644
index 00000000..2d8d1c63
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java
@@ -0,0 +1,28 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about "generic" servers.
+// a server is "generic", when only name, server ID and gems are shown
+// in the third column of the tab HUD
+
+public class ServerWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public ServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
new file mode 100644
index 00000000..88ba8022
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java
@@ -0,0 +1,75 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about a skill and some stats,
+// as seen in the rightmost column of the default HUD
+
+public class SkillsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW,
+ Formatting.BOLD);
+
+ // match the skill entry
+ // group 1: skill name and level
+ // group 2: progress to next level (without "%")
+ private static final Pattern SKILL_PATTERN = Pattern.compile("\\S*: ([A-Za-z]* [0-9]*): ([0-9.MAX]*)%?");
+
+ public SkillsWidget() {
+ super(TITLE, Formatting.YELLOW.getColorValue());
+
+ Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN);
+ Component progress;
+ if (m == null) {
+ progress = new ProgressComponent();
+ } else {
+
+ String skill = m.group(1);
+ String pcntStr = m.group(2);
+
+ if (!pcntStr.equals("MAX")) {
+ float pcnt = Float.parseFloat(pcntStr);
+ progress = new ProgressComponent(Ico.LANTERN, Text.of(skill),
+ Text.of(pcntStr + "%"), pcnt, Formatting.GOLD.getColorValue());
+ } else {
+ progress = new IcoFatTextComponent(Ico.LANTERN, Text.of(skill),
+ Text.literal(pcntStr).formatted(Formatting.RED));
+ }
+ }
+
+ this.addComponent(progress);
+
+ Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE);
+ IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed);
+ Text strength = Widget.simpleEntryText(68, "STR", Formatting.RED);
+ IcoTextComponent str = new IcoTextComponent(Ico.SWORD, strength);
+ Text critDmg = Widget.simpleEntryText(69, "CCH", Formatting.BLUE);
+ IcoTextComponent cdg = new IcoTextComponent(Ico.SWORD, critDmg);
+ Text critCh = Widget.simpleEntryText(70, "CDG", Formatting.BLUE);
+ IcoTextComponent cch = new IcoTextComponent(Ico.SWORD, critCh);
+ Text aSpeed = Widget.simpleEntryText(71, "ASP", Formatting.YELLOW);
+ IcoTextComponent asp = new IcoTextComponent(Ico.HOE, aSpeed);
+
+ TableComponent tc = new TableComponent(2, 3, Formatting.YELLOW.getColorValue());
+ tc.addToCell(0, 0, spd);
+ tc.addToCell(0, 1, str);
+ tc.addToCell(0, 2, asp);
+ tc.addToCell(1, 0, cdg);
+ tc.addToCell(1, 1, cch);
+ this.addComponent(tc);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
new file mode 100644
index 00000000..d47849c3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java
@@ -0,0 +1,22 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows how meny pelts you have (farming island)
+
+public class TrapperWidget extends Widget {
+ private static final MutableText TITLE = Text.literal("Trapper").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public TrapperWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.LEATHER, "Pelts:", Formatting.AQUA, 46);
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
new file mode 100644
index 00000000..ef7c21d0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java
@@ -0,0 +1,47 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+// this widget shows info about ongoing profile/account upgrades
+// or not, if there aren't any
+// TODO: not very pretty atm
+
+public class UpgradeWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Upgrade Info").formatted(Formatting.GOLD,
+ Formatting.BOLD);
+
+ public UpgradeWidget(String footertext) {
+ super(TITLE, Formatting.GOLD.getColorValue());
+ if (footertext == null) {
+ this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY)));
+ this.pack();
+ return;
+ }
+
+ if (!footertext.contains("Upgrades")) {
+ this.addComponent(new PlainTextComponent(Text.of("Currently no upgrades...")));
+ this.pack();
+ return;
+ }
+
+ String interesting = footertext.split("Upgrades")[1];
+ String[] lines = interesting.split("\n");
+
+ for (int i = 1; i < lines.length; i++) {
+ if (lines[i].trim().length() < 3) { // empty line is §s
+ break;
+ }
+ IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, Text.of(lines[i]));
+ this.addComponent(itc);
+ }
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java
new file mode 100644
index 00000000..ec6a35fb
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java
@@ -0,0 +1,57 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.HashMap;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Pair;
+
+// shows the volcano status (crimson isle)
+
+public class VolcanoWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Volcano Status").formatted(Formatting.AQUA,
+ Formatting.BOLD);
+
+ private static final HashMap<String, Pair<ItemStack, Formatting>> BOOM_TYPE = new HashMap<>();
+
+ static {
+ BOOM_TYPE.put("INACTIVE",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY));
+ BOOM_TYPE.put("CHILL",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.ICE), Formatting.AQUA));
+ BOOM_TYPE.put("LOW",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY));
+ BOOM_TYPE.put("DISRUPTIVE",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE));
+ BOOM_TYPE.put("MEDIUM",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW));
+ BOOM_TYPE.put("HIGH",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD));
+ BOOM_TYPE.put("EXPLOSIVE",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.TNT), Formatting.RED));
+ BOOM_TYPE.put("CATACLYSMIC",
+ new Pair<ItemStack, Formatting>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED));
+ }
+
+ public VolcanoWidget() {
+ super(TITLE, Formatting.AQUA.getColorValue());
+
+ String s = PlayerListMgr.strAt(58);
+ if (s == null) {
+ this.addComponent(new IcoTextComponent());
+ } else {
+ Pair<ItemStack, Formatting> p = BOOM_TYPE.get(s);
+ this.addComponent(new IcoTextComponent(p.getLeft(), Text.literal(s).formatted(p.getRight())));
+ }
+
+ this.pack();
+
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
new file mode 100644
index 00000000..33f77933
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java
@@ -0,0 +1,203 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget;
+
+import java.util.ArrayList;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Abstract base class for a Widget.
+ * Widgets are containers for components with a border and a title.
+ * Their size is dependent on the components inside,
+ * the position may be changed after construction.
+ */
+public abstract class Widget {
+
+ private ArrayList<Component> components = new ArrayList<>();
+ private int w = 0, h = 0;
+ private int x = 0, y = 0;
+ private int color;
+ private Text title;
+
+ private static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+
+ static final int BORDER_SZE_N = txtRend.fontHeight + 4;
+ static final int BORDER_SZE_S = 4;
+ static final int BORDER_SZE_W = 4;
+ static final int BORDER_SZE_E = 4;
+ static final int COL_BG_BOX = 0xc00c0c0c;
+
+ public Widget(MutableText title, Integer colorValue) {
+ this.title = title;
+ this.color = 0xff000000 | colorValue;
+ }
+
+ public final void addComponent(Component c) {
+ components.add(c);
+ }
+
+ /**
+ * Shorthand function for simple components.
+ * If the entry at idx has the format "<textA>: <textB>", an IcoTextComponent is added as such:
+ * [ico] [string] [textB.formatted(fmt)]
+ */
+ public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, int idx) {
+ Text txt = Widget.simpleEntryText(idx, string, fmt);
+ this.addComponent(new IcoTextComponent(ico, txt));
+ }
+
+ /**
+ * Calculate the size of this widget.
+ * <b>Must be called before returning from the widget constructor and after all components are added!</b>
+ */
+ public final void pack() {
+ for (Component c : components) {
+ h += c.getHeight() + Component.PAD_L;
+ w = Math.max(w, c.getWidth() + Component.PAD_S);
+ }
+
+ h -= Component.PAD_L / 2; // less padding after lowest/last component
+ h += BORDER_SZE_N + BORDER_SZE_S - 2;
+ w += BORDER_SZE_E + BORDER_SZE_W;
+
+ // min width is dependent on title
+ w = Math.max(w, BORDER_SZE_W + BORDER_SZE_E + Widget.txtRend.getWidth(title) + 4 + 4 + 1);
+ }
+
+ public final void setX(int x) {
+ this.x = x;
+ }
+
+ public final int getY() {
+ return this.y;
+ }
+
+ public final int getX() {
+ return this.x;
+ }
+
+ public final void setY(int y) {
+ this.y = y;
+ }
+
+ public final int getWidth() {
+ return this.w;
+ }
+
+ public final int getHeight() {
+ return this.h;
+ }
+
+ /**
+ * Draw this widget with a background
+ */
+ public final void render(DrawContext context) {
+ this.render(context, true);
+ }
+
+ /**
+ * Draw this widget, possibly with a background
+ */
+ public final void render(DrawContext context, boolean hasBG) {
+ MatrixStack ms = context.getMatrices();
+
+ // not sure if this is the way to go, but it fixes Z-layer issues
+ // like blocks being rendered behind the BG and the hotbar clipping into things
+ RenderSystem.enableDepthTest();
+ ms.push();
+
+ float scale = SkyblockerConfig.get().general.tabHud.tabHudScale / 100f;
+ ms.scale(scale, scale, 1);
+
+ // move above other UI elements
+ ms.translate(0, 0, 200);
+ if (hasBG) {
+ context.fill(x + 1, y, x + w - 1, y + h, COL_BG_BOX);
+ context.fill(x, y + 1, x + 1, y + h - 1, COL_BG_BOX);
+ context.fill(x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX);
+ }
+ // move above background (if exists)
+ ms.translate(0, 0, 100);
+
+ int strHeightHalf = Widget.txtRend.fontHeight / 2;
+ int strAreaWidth = Widget.txtRend.getWidth(title) + 4;
+
+ context.drawText(txtRend, title, x + 8, y + 2, this.color, false);
+
+ this.drawHLine(context, x + 2, y + 1 + strHeightHalf, 4);
+ this.drawHLine(context, x + 2 + strAreaWidth + 4, y + 1 + strHeightHalf, w - 4 - 4 - strAreaWidth);
+ this.drawHLine(context, x + 2, y + h - 2, w - 4);
+
+ this.drawVLine(context, x + 1, y + 2 + strHeightHalf, h - 4 - strHeightHalf);
+ this.drawVLine(context, x + w - 2, y + 2 + strHeightHalf, h - 4 - strHeightHalf);
+
+ int yOffs = y + BORDER_SZE_N;
+
+ for (Component c : components) {
+ c.render(context, x + BORDER_SZE_W, yOffs);
+ yOffs += c.getHeight() + Component.PAD_L;
+ }
+ // pop manipulations above
+ ms.pop();
+ RenderSystem.disableDepthTest();
+ }
+
+ private void drawHLine(DrawContext context, int xpos, int ypos, int width) {
+ context.fill(xpos, ypos, xpos + width, ypos + 1, this.color);
+ }
+
+ private void drawVLine(DrawContext context, int xpos, int ypos, int height) {
+ context.fill(xpos, ypos, xpos + 1, ypos + height, this.color);
+ }
+
+ /**
+ * If the entry at idx has the format "[textA]: [textB]", the following is returned:
+ * [entryName] [textB.formatted(contentFmt)]
+ */
+ public static Text simpleEntryText(int idx, String entryName, Formatting contentFmt) {
+
+ String src = PlayerListMgr.strAt(idx);
+
+ if (src == null) {
+ return null;
+ }
+
+ int cidx = src.indexOf(':');
+ if (cidx == -1) {
+ return null;
+ }
+
+ src = src.substring(src.indexOf(':') + 1);
+ return Widget.simpleEntryText(src, entryName, contentFmt);
+ }
+
+ /**
+ * @return [entryName] [entryContent.formatted(contentFmt)]
+ */
+ public static Text simpleEntryText(String entryContent, String entryName, Formatting contentFmt) {
+ return Text.literal(entryName).append(Text.literal(entryContent).formatted(contentFmt));
+ }
+
+ /**
+ * @return the entry at idx as unformatted Text
+ */
+ public static Text plainEntryText(int idx) {
+ String str = PlayerListMgr.strAt(idx);
+ if (str == null) {
+ return null;
+ }
+ return Text.of(str);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java
new file mode 100644
index 00000000..850cb3d2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java
@@ -0,0 +1,32 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.render.item.ItemRenderer;
+
+/**
+ * Abstract base class for a component that may be added to a Widget.
+ */
+public abstract class Component {
+
+ static final int ICO_DIM = 16;
+ public static final int PAD_S = 2;
+ public static final int PAD_L = 4;
+
+ static TextRenderer txtRend = MinecraftClient.getInstance().textRenderer;
+
+ // these should always be the content dimensions without any padding.
+ int width, height;
+
+ public abstract void render(DrawContext context, int x, int y);
+
+ public int getWidth() {
+ return this.width;
+ }
+
+ public int getHeight() {
+ return this.height;
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java
new file mode 100644
index 00000000..afd05c26
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java
@@ -0,0 +1,45 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Component that consists of an icon and two lines of text
+ */
+public class IcoFatTextComponent extends Component {
+
+ private static final int ICO_OFFS = 1;
+
+ private ItemStack ico;
+ private Text line1, line2;
+
+ public IcoFatTextComponent(ItemStack ico, Text l1, Text l2) {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.line1 = l1;
+ this.line2 = l2;
+
+ if (l1 == null || l2 == null) {
+ this.ico = Ico.BARRIER;
+ this.line1 = Text.literal("No data").formatted(Formatting.GRAY);
+ this.line2 = Text.literal("No data").formatted(Formatting.GRAY);
+ }
+
+ this.width = ICO_DIM + PAD_L + Math.max(txtRend.getWidth(this.line1), txtRend.getWidth(this.line2));
+ this.height = txtRend.fontHeight + PAD_S + txtRend.fontHeight;
+ }
+
+ public IcoFatTextComponent() {
+ this(null, null, null);
+ }
+
+ @Override
+ public void render(DrawContext context, int x, int y) {
+ context.drawItem(ico, x, y + ICO_OFFS);
+ context.drawText(txtRend, line1, x + ICO_DIM + PAD_L, y, 0xffffffff, false);
+ context.drawText(txtRend, line2, x + ICO_DIM + PAD_L, y + txtRend.fontHeight + PAD_S, 0xffffffff, false);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java
new file mode 100644
index 00000000..7ab92dd5
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java
@@ -0,0 +1,40 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Component that consists of an icon and a line of text.
+ */
+public class IcoTextComponent extends Component {
+
+ private ItemStack ico;
+ private Text text;
+
+ public IcoTextComponent(ItemStack ico, Text txt) {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.text = txt;
+
+ if (txt == null) {
+ this.ico = Ico.BARRIER;
+ this.text = Text.literal("No data").formatted(Formatting.GRAY);
+ }
+
+ this.width = ICO_DIM + PAD_L + txtRend.getWidth(this.text);
+ this.height = ICO_DIM;
+ }
+
+ public IcoTextComponent() {
+ this(null, null);
+ }
+
+ @Override
+ public void render(DrawContext context, int x, int y) {
+ context.drawItem(ico, x, y);
+ context.drawText(txtRend, text, x + ICO_DIM + PAD_L, y + 5, 0xffffffff, false);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java
new file mode 100644
index 00000000..34e0268b
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java
@@ -0,0 +1,30 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Component that consists of a line of text.
+ */
+public class PlainTextComponent extends Component {
+
+ private Text text;
+
+ public PlainTextComponent(Text txt) {
+ this.text = txt;
+
+ if (txt == null) {
+ this.text = Text.literal("No data").formatted(Formatting.GRAY);
+ }
+
+ this.width = PAD_S + txtRend.getWidth(this.text); // looks off without padding
+ this.height = txtRend.fontHeight;
+ }
+
+ @Override
+ public void render(DrawContext context, int x, int y) {
+ context.drawText(txtRend, text, x + PAD_S, y, 0xffffffff, false);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
new file mode 100644
index 00000000..fd66ec73
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java
@@ -0,0 +1,33 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.PlayerSkinDrawer;
+import net.minecraft.client.network.PlayerListEntry;
+import net.minecraft.util.Identifier;
+
+/**
+ * Component that consists of a player's skin icon and their name
+ */
+public class PlayerComponent extends Component {
+
+ private static final int SKIN_ICO_DIM = 8;
+
+ private String name;
+ private Identifier tex;
+
+ public PlayerComponent(PlayerListEntry ple) {
+
+ name = ple.getProfile().getName();
+ tex = ple.getSkinTexture();
+
+ this.width = SKIN_ICO_DIM + PAD_S + txtRend.getWidth(name);
+ this.height = txtRend.fontHeight;
+ }
+
+ @Override
+ public void render(DrawContext context, int x, int y) {
+ PlayerSkinDrawer.draw(context, tex, x, y, SKIN_ICO_DIM);
+ context.drawText(txtRend, name, x + SKIN_ICO_DIM + PAD_S, y, 0xffffffff, false);
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java
new file mode 100644
index 00000000..a7cc8d12
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java
@@ -0,0 +1,69 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+
+/**
+ * Component that consists of an icon, some text and a progress bar.
+ * The progress bar either shows the fill percentage or custom text.
+ * NOTICE: pcnt is 0-100, not 0-1!
+ */
+public class ProgressComponent extends Component {
+
+ private static final int BAR_WIDTH = 100;
+ private static final int BAR_HEIGHT = txtRend.fontHeight + 3;
+ private static final int ICO_OFFS = 4;
+ private static final int COL_BG_BAR = 0xf0101010;
+
+ private ItemStack ico;
+ private Text desc, bar;
+ private float pcnt;
+ private int color;
+ private int barW;
+
+ public ProgressComponent(ItemStack ico, Text d, Text b, float pcnt, int color) {
+ this.ico = (ico == null) ? Ico.BARRIER : ico;
+ this.desc = d;
+ this.bar = b;
+ this.color = 0xff000000 | color;
+ this.pcnt = pcnt;
+
+ if (d == null || b == null) {
+ this.ico = Ico.BARRIER;
+ this.desc = Text.literal("No data").formatted(Formatting.GRAY);
+ this.bar = Text.literal("---").formatted(Formatting.GRAY);
+ this.pcnt = 100f;
+ this.color = 0xff000000 | Formatting.DARK_GRAY.getColorValue();
+ }
+
+ this.barW = BAR_WIDTH;
+ this.width = ICO_DIM + PAD_L + Math.max(this.barW, txtRend.getWidth(this.desc));
+ this.height = txtRend.fontHeight + PAD_S + 2 + txtRend.fontHeight + 2;
+ }
+
+ public ProgressComponent(ItemStack ico, Text text, float pcnt, int color) {
+ this(ico, text, Text.of(pcnt + "%"), pcnt, color);
+ }
+
+ public ProgressComponent() {
+ this(null, null, null, 100, 0);
+ }
+
+ @Override
+ public void render(DrawContext context, int x, int y) {
+ context.drawItem(ico, x, y + ICO_OFFS);
+ context.drawText(txtRend, desc, x + ICO_DIM + PAD_L, y, 0xffffffff, false);
+
+ int barX = x + ICO_DIM + PAD_L;
+ int barY = y + txtRend.fontHeight + PAD_S;
+ int endOffsX = ((int) (this.barW * (this.pcnt / 100f)));
+ context.fill(barX + endOffsX, barY, barX + this.barW, barY + BAR_HEIGHT, COL_BG_BAR);
+ context.fill(barX, barY, barX + endOffsX, barY + BAR_HEIGHT,
+ this.color);
+ context.drawTextWithShadow(txtRend, bar, barX + 3, barY + 2, 0xffffffff);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java
new file mode 100644
index 00000000..30287dc0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java
@@ -0,0 +1,58 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component;
+
+import net.minecraft.client.gui.DrawContext;
+
+/**
+ * Meta-Component that consists of a grid of other components
+ * Grid cols are separated by lines.
+ */
+public class TableComponent extends Component {
+
+ private Component[][] comps;
+ private int color;
+ private int cols, rows;
+ private int cellW, cellH;
+
+ public TableComponent(int w, int h, int col) {
+ comps = new Component[w][h];
+ color = 0xff000000 | col;
+ cols = w;
+ rows = h;
+ }
+
+ public void addToCell(int x, int y, Component c) {
+ this.comps[x][y] = c;
+
+ // pad extra to add a vertical line later
+ this.cellW = Math.max(this.cellW, c.width + PAD_S + PAD_L);
+
+ // assume all rows are equally high so overwriting doesn't matter
+ // if this wasn't the case, drawing would need more math
+ // not doing any of that if it's not needed
+ this.cellH = c.height + PAD_S;
+
+ this.width = this.cellW * this.cols;
+ this.height = (this.cellH * this.rows) - PAD_S / 2;
+
+ }
+
+ @Override
+ public void render(DrawContext context, int xpos, int ypos) {
+ for (int x = 0; x < cols; x++) {
+ for (int y = 0; y < rows; y++) {
+ if (comps[x][y] != null) {
+ comps[x][y].render(context, xpos + (x * cellW), ypos + y * cellH);
+ }
+ }
+ // add a line before the col if we're not drawing the first one
+ if (x != 0) {
+ int lineX1 = xpos + (x * cellW) - PAD_S - 1;
+ int lineX2 = xpos + (x * cellW) - PAD_S;
+ int lineY1 = ypos + 1;
+ int lineY2 = ypos + this.height - PAD_S - 1; // not sure why but it looks correct
+ context.fill(lineX1, lineY1, lineX2, lineY2, this.color);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java
new file mode 100644
index 00000000..287b25b1
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java
@@ -0,0 +1,27 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class AdvertisementWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Advertisement").formatted(Formatting.DARK_AQUA,
+ Formatting.BOLD);
+
+ public AdvertisementWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ for (int i = 73; i < 80; i++) {
+ Text text = PlayerListMgr.textAt(i);
+ if (text != null)
+ this.addComponent(new PlainTextComponent(text));
+ }
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java
new file mode 100644
index 00000000..667bc154
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java
@@ -0,0 +1,55 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class GoodToKnowWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Good To Know").formatted(Formatting.BLUE, Formatting.BOLD);
+
+ public GoodToKnowWidget() {
+ super(TITLE, Formatting.BLUE.getColorValue());
+
+ // After you progress further the tab adds more info so we need to be careful of
+ // that
+ // In beginning it only shows montezuma, then timecharms and enigma souls are
+ // added
+ Text pos49 = PlayerListMgr.textAt(49); // Can be times visited rift
+ Text pos51 = PlayerListMgr.textAt(51); // Can be lifetime motes or visited rift
+ Text pos53 = PlayerListMgr.textAt(53); // Can be lifetime motes
+
+ int visitedRiftPos = 0;
+ int lifetimeMotesPos = 0;
+
+ // Check each position to see what is or isn't there so we don't try adding
+ // invalid components
+ if (pos49.getString().contains("times"))
+ visitedRiftPos = 49;
+ if (pos51.getString().contains("Motes"))
+ lifetimeMotesPos = 51;
+ if (pos51.getString().contains("times"))
+ visitedRiftPos = 51;
+ if (pos53.getString().contains("Motes"))
+ lifetimeMotesPos = 53;
+
+ Text timesVisitedRift = (visitedRiftPos == 51) ? pos51 : (visitedRiftPos == 49) ? pos49 : null;
+ Text lifetimeMotesEarned = (lifetimeMotesPos == 53) ? pos53 : (lifetimeMotesPos == 51) ? pos51 : null;
+
+ if (visitedRiftPos != 0) {
+ this.addComponent(new IcoTextComponent(Ico.EXPERIENCE_BOTTLE,
+ Text.literal("Visited Rift: ").append(timesVisitedRift)));
+ }
+
+ if (lifetimeMotesPos != 0) {
+ this.addComponent(
+ new IcoTextComponent(Ico.PINK_DYE, Text.literal("Lifetime Earned: ").append(lifetimeMotesEarned)));
+ }
+
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java
new file mode 100644
index 00000000..5460de49
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java
@@ -0,0 +1,19 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class RiftProfileWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.DARK_AQUA, Formatting.BOLD);
+
+ public RiftProfileWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61);
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java
new file mode 100644
index 00000000..9ce12e76
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java
@@ -0,0 +1,97 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.math.MathHelper;
+
+public class RiftProgressWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Rift Progress").formatted(Formatting.BLUE, Formatting.BOLD);
+
+ private static final Pattern TIMECHARMS_PATTERN = Pattern.compile("Timecharms: (?<current>[0-9]+)\\/(?<total>[0-9]+)");
+ private static final Pattern ENIGMA_SOULS_PATTERN = Pattern.compile("Enigma Souls: (?<current>[0-9]+)\\/(?<total>[0-9]+)");
+ private static final Pattern MONTEZUMA_PATTERN = Pattern.compile("Montezuma: (?<current>[0-9]+)\\/(?<total>[0-9]+)");
+
+ public RiftProgressWidget() {
+ super(TITLE, Formatting.BLUE.getColorValue());
+
+ // After you progress further the tab adds more info so we need to be careful of
+ // that
+ // In beginning it only shows montezuma, then timecharms and enigma souls are
+ // added
+ String pos45 = PlayerListMgr.strAt(45); // Can be Montezuma or Timecharms
+ String pos46 = PlayerListMgr.strAt(46); // Can be Enigma Souls or Empty
+ String pos47 = PlayerListMgr.strAt(47); // Can be Montezuma or "Good to know" heading
+
+ boolean hasTimecharms = false;
+ boolean hasEnigmaSouls = false;
+ int montezumaPos = 0;
+
+ // Check each position to see what is or isn't there so we don't try adding
+ // invalid components
+ if (pos45.contains("Timecharms"))
+ hasTimecharms = true;
+ if (pos46.contains("Enigma Souls"))
+ hasEnigmaSouls = true;
+
+ // Small ternary to account for positions, defaults to -1 if it for some reason
+ // does not exist (which shouldn't be the case!)
+ montezumaPos = (pos47.contains("Montezuma")) ? 47 : (pos45.contains("Montezuma")) ? 45 : -1;
+
+ if (hasTimecharms) {
+ Matcher m = PlayerListMgr.regexAt(45, TIMECHARMS_PATTERN);
+
+ int current = Integer.parseInt(m.group("current"));
+ int total = Integer.parseInt(m.group("total"));
+ float pcnt = ((float) current / (float) total) * 100f;
+ Text progressText = Text.literal(current + "/" + total);
+
+ ProgressComponent pc = new ProgressComponent(Ico.NETHER_STAR, Text.literal("Timecharms"), progressText,
+ pcnt, pcntToCol(pcnt));
+
+ this.addComponent(pc);
+ }
+
+ if (hasEnigmaSouls) {
+ Matcher m = PlayerListMgr.regexAt(46, ENIGMA_SOULS_PATTERN);
+
+ int current = Integer.parseInt(m.group("current"));
+ int total = Integer.parseInt(m.group("total"));
+ float pcnt = ((float) current / (float) total) * 100f;
+ Text progressText = Text.literal(current + "/" + total);
+
+ ProgressComponent pc = new ProgressComponent(Ico.HEART_OF_THE_SEA, Text.literal("Enigma Souls"),
+ progressText, pcnt, pcntToCol(pcnt));
+
+ this.addComponent(pc);
+ }
+
+ if (montezumaPos != -1) {
+ Matcher m = PlayerListMgr.regexAt(montezumaPos, MONTEZUMA_PATTERN);
+
+ int current = Integer.parseInt(m.group("current"));
+ int total = Integer.parseInt(m.group("total"));
+ float pcnt = ((float) current / (float) total) * 100f;
+ Text progressText = Text.literal(current + "/" + total);
+
+ ProgressComponent pc = new ProgressComponent(Ico.BONE, Text.literal("Montezuma"), progressText, pcnt,
+ pcntToCol(pcnt));
+
+ this.addComponent(pc);
+ }
+
+ this.pack();
+ }
+
+ private static int pcntToCol(float pcnt) {
+ return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java
new file mode 100644
index 00000000..2ac2a35d
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java
@@ -0,0 +1,26 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Special version of the server info widget for the rift!
+ *
+ */
+public class RiftServerInfoWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD);
+
+ public RiftServerInfoWidget() {
+ super(TITLE, Formatting.LIGHT_PURPLE.getColorValue());
+
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.LIGHT_PURPLE, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+
+ this.pack();
+ }
+
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java
new file mode 100644
index 00000000..ef5876f2
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java
@@ -0,0 +1,41 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class RiftStatsWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Stats").formatted(Formatting.DARK_AQUA, Formatting.BOLD);
+
+ public RiftStatsWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ Text riftDamage = Widget.simpleEntryText(64, "RDG", Formatting.DARK_PURPLE);
+ IcoTextComponent rdg = new IcoTextComponent(Ico.DIASWORD, riftDamage);
+
+ Text speed = Widget.simpleEntryText(65, "SPD", Formatting.WHITE);
+ IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed);
+
+ Text intelligence = Widget.simpleEntryText(66, "INT", Formatting.AQUA);
+ IcoTextComponent intel = new IcoTextComponent(Ico.ENCHANTED_BOOK, intelligence);
+
+ Text manaRegen = Widget.simpleEntryText(67, "MRG", Formatting.AQUA);
+ IcoTextComponent mrg = new IcoTextComponent(Ico.DIAMOND, manaRegen);
+
+ TableComponent tc = new TableComponent(2, 2, Formatting.AQUA.getColorValue());
+ tc.addToCell(0, 0, rdg);
+ tc.addToCell(0, 1, spd);
+ tc.addToCell(1, 0, intel);
+ tc.addToCell(1, 1, mrg);
+
+ this.addComponent(tc);
+ this.pack();
+ }
+
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java
new file mode 100644
index 00000000..5dcc08c0
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java
@@ -0,0 +1,20 @@
+package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift;
+
+import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget;
+import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+public class ShenWidget extends Widget {
+
+ private static final MutableText TITLE = Text.literal("Shen's Countdown").formatted(Formatting.DARK_AQUA, Formatting.BOLD);
+
+ public ShenWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+
+ this.addComponent(new PlainTextComponent(Text.literal(PlayerListMgr.strAt(70))));
+ this.pack();
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/FrustumUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/FrustumUtils.java
index 6973aa1e..9ea90c16 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/FrustumUtils.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/FrustumUtils.java
@@ -1,6 +1,7 @@
package me.xmrvizzy.skyblocker.utils;
import me.xmrvizzy.skyblocker.mixin.AccessorWorldRenderer;
+import me.xmrvizzy.skyblocker.mixin.accessor.FrustumInvoker;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Frustum;
import net.minecraft.util.math.Box;
@@ -14,4 +15,8 @@ public class FrustumUtils {
public static boolean isBoxVisible(Box box) {
return getFrustum().isVisible(box);
}
+
+ public static boolean isVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) {
+ return ((FrustumInvoker) getFrustum()).isVisible(minX, minY, minZ, maxX, maxY, maxZ);
+ }
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/MessageScheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/MessageScheduler.java
new file mode 100644
index 00000000..ac6aa293
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/MessageScheduler.java
@@ -0,0 +1,63 @@
+package me.xmrvizzy.skyblocker.utils;
+
+import net.minecraft.client.MinecraftClient;
+
+/**
+ * A scheduler for sending chat messages or commands. Use the instance in {@link me.xmrvizzy.skyblocker.SkyblockerMod#messageScheduler SkyblockerMod.messageScheduler}. Do not instantiate this class.
+ */
+@SuppressWarnings("deprecation")
+public class MessageScheduler extends Scheduler {
+ /**
+ * The minimum delay that the server will accept between chat messages.
+ */
+ private static final int MIN_DELAY = 200;
+ /**
+ * The timestamp of the last message send,
+ */
+ private long lastMessage = 0;
+
+ /**
+ * Sends a chat message or command after the minimum cooldown. Prefer this method to send messages or commands to the server.
+ *
+ * @param message the message to send
+ */
+ public void sendMessageAfterCooldown(String message) {
+ if (lastMessage + MIN_DELAY < System.currentTimeMillis()) {
+ sendMessage(message);
+ lastMessage = System.currentTimeMillis();
+ } else {
+ queueMessage(message, 0);
+ }
+ }
+
+ private void sendMessage(String message) {
+ if (MinecraftClient.getInstance().player != null) {
+ MinecraftClient.getInstance().inGameHud.getChatHud().addToMessageHistory(message);
+ if (message.startsWith("/")) {
+ MinecraftClient.getInstance().player.networkHandler.sendCommand(message.substring(1));
+ } else {
+ MinecraftClient.getInstance().player.networkHandler.sendChatMessage(message);
+ }
+ }
+ }
+
+ /**
+ * Queues a chat message or command to send in {@code delay} ticks. Use this method to send messages or commands a set time in the future. The minimum cooldown is still respected.
+ *
+ * @param message the message to send
+ * @param delay the delay before sending the message in ticks
+ */
+ public void queueMessage(String message, int delay) {
+ schedule(() -> sendMessage(message), delay);
+ }
+
+ @Override
+ protected boolean runTask(Runnable task) {
+ if (lastMessage + MIN_DELAY < System.currentTimeMillis()) {
+ task.run();
+ lastMessage = System.currentTimeMillis();
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java b/src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java
new file mode 100644
index 00000000..29b39aa3
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java
@@ -0,0 +1,101 @@
+package me.xmrvizzy.skyblocker.utils;
+
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.text.Text;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.TransportException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * Initializes the NEU repo, which contains item metadata and fairy souls location data. Clones the repo if it does not exist and checks for updates. Use {@link #runAsyncAfterLoad(Runnable)} to run code after the repo is initialized.
+ */
+public class NEURepo {
+ private static final Logger LOGGER = LoggerFactory.getLogger(NEURepo.class);
+ public static final String REMOTE_REPO_URL = "https://github.com/NotEnoughUpdates/NotEnoughUpdates-REPO.git";
+ public static final Path LOCAL_REPO_DIR = SkyblockerMod.CONFIG_DIR.resolve("item-repo");
+ private static final CompletableFuture<Void> REPO_INITIALIZED = initRepository();
+
+ /**
+ * Adds command to update repository manually from ingame.
+ * <p></p>
+ * TODO A button could be added to the settings menu that will trigger this command.
+ */
+ public static void init() {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) ->
+ dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE)
+ .then(ClientCommandManager.literal("updaterepository").executes(context -> {
+ deleteAndDownloadRepository();
+ return 1;
+ }))));
+ }
+
+ private static CompletableFuture<Void> initRepository() {
+ return CompletableFuture.runAsync(() -> {
+ try {
+ if (Files.isDirectory(NEURepo.LOCAL_REPO_DIR)) {
+ try (Git localRepo = Git.open(NEURepo.LOCAL_REPO_DIR.toFile())) {
+ localRepo.pull().setRebase(true).call();
+ LOGGER.info("[Skyblocker] NEU Repository Updated");
+ }
+ } else {
+ Git.cloneRepository().setURI(REMOTE_REPO_URL).setDirectory(NEURepo.LOCAL_REPO_DIR.toFile()).setBranchesToClone(List.of("refs/heads/master")).setBranch("refs/heads/master").call().close();
+ LOGGER.info("[Skyblocker] NEU Repository Downloaded");
+ }
+ } catch (TransportException e){
+ LOGGER.error("[Skyblocker] Transport operation failed. Most likely unable to connect to the remote NEU repo on github", e);
+ } catch (RepositoryNotFoundException e) {
+ LOGGER.warn("[Skyblocker] Local NEU Repository not found or corrupted, downloading new one", e);
+ deleteAndDownloadRepository();
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Encountered unknown exception while initializing NEU Repository", e);
+ }
+ });
+ }
+
+ private static void deleteAndDownloadRepository() {
+ CompletableFuture.runAsync(() -> {
+ try {
+ ItemRegistry.filesImported = false;
+ File dir = NEURepo.LOCAL_REPO_DIR.toFile();
+ recursiveDelete(dir);
+ } catch (Exception ex) {
+ if (MinecraftClient.getInstance().player != null)
+ MinecraftClient.getInstance().player.sendMessage(Text.translatable("skyblocker.updaterepository.failed"), false);
+ return;
+ }
+ initRepository();
+ });
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ private static void recursiveDelete(File dir) {
+ File[] children;
+ if (dir.isDirectory() && !Files.isSymbolicLink(dir.toPath()) && (children = dir.listFiles()) != null) {
+ for (File child : children) {
+ recursiveDelete(child);
+ }
+ }
+ dir.delete();
+ }
+
+ /**
+ * Runs the given runnable after the NEU repo is initialized.
+ * @param runnable the runnable to run
+ * @return a completable future of the given runnable
+ */
+ public static CompletableFuture<Void> runAsyncAfterLoad(Runnable runnable) {
+ return REPO_INITIALIZED.thenRunAsync(runnable);
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/RenderHelper.java b/src/main/java/me/xmrvizzy/skyblocker/utils/RenderHelper.java
new file mode 100644
index 00000000..6fa93735
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/RenderHelper.java
@@ -0,0 +1,86 @@
+package me.xmrvizzy.skyblocker.utils;
+
+import me.x150.renderer.render.Renderer3d;
+import me.xmrvizzy.skyblocker.mixin.accessor.BeaconBlockEntityRendererInvoker;
+import me.xmrvizzy.skyblocker.utils.title.Title;
+import me.xmrvizzy.skyblocker.utils.title.TitleContainer;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer;
+import net.minecraft.sound.SoundEvent;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+
+import java.awt.*;
+
+public class RenderHelper {
+ private static final Vec3d ONE = new Vec3d(1, 1, 1);
+
+ public static void renderFilledThroughWallsWithBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
+ renderFilledThroughWalls(context, pos, colorComponents, alpha);
+ renderBeaconBeam(context, pos, colorComponents);
+ }
+
+ public static void renderFilledThroughWalls(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
+ Renderer3d.renderThroughWalls();
+ renderFilled(context, pos, colorComponents, alpha);
+ Renderer3d.stopRenderThroughWalls();
+ }
+
+ public static void renderFilledIfVisible(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
+ if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) {
+ renderFilled(context, pos, colorComponents, alpha);
+ }
+ }
+
+ public static void renderFilled(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
+ Renderer3d.renderFilled(context.matrixStack(), new Color(colorComponents[0], colorComponents[1], colorComponents[2], alpha), Vec3d.of(pos), ONE);
+ }
+
+ public static void renderBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents) {
+ context.matrixStack().push();
+ context.matrixStack().translate(pos.getX() - context.camera().getPos().x, pos.getY() - context.camera().getPos().y, pos.getZ() - context.camera().getPos().z);
+ BeaconBlockEntityRendererInvoker.renderBeam(context.matrixStack(), context.consumers(), context.tickDelta(), context.world().getTime(), 0, BeaconBlockEntityRenderer.MAX_BEAM_HEIGHT, colorComponents);
+ context.matrixStack().pop();
+ }
+
+ public static void displayTitleAndPlaySound(int stayTicks, int fadeOutTicks, String titleKey, Formatting formatting) {
+ MinecraftClient.getInstance().inGameHud.setTitleTicks(0, stayTicks, fadeOutTicks);
+ MinecraftClient.getInstance().inGameHud.setTitle(Text.translatable(titleKey).formatted(formatting));
+ playNotificationSound();
+ }
+
+ /**
+ * Adds the title to {@link TitleContainer} and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already.
+ * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller.
+ *
+ * @param title the title
+ */
+ public static void displayInTitleContainerAndPlaySound(Title title) {
+ if (TitleContainer.addTitle(title)) {
+ playNotificationSound();
+ }
+ }
+
+ /**
+ * Adds the title to {@link TitleContainer} for a set number of ticks and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already.
+ * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller.
+ *
+ * @param title the title
+ * @param ticks the number of ticks the title will remain
+ */
+ public static void displayInTitleContainerAndPlaySound(Title title, int ticks) {
+ if (TitleContainer.addTitle(title, ticks)) {
+ playNotificationSound();
+ }
+ }
+
+ private static void playNotificationSound() {
+ if (MinecraftClient.getInstance().player != null) {
+ MinecraftClient.getInstance().player.playSound(SoundEvent.of(new Identifier("entity.experience_orb.pickup")), 100f, 0.1f);
+ }
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Scheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Scheduler.java
index 16e5b023..7b19e284 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/Scheduler.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Scheduler.java
@@ -1,60 +1,116 @@
package me.xmrvizzy.skyblocker.utils;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.Screen;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.PriorityQueue;
+import java.util.function.Supplier;
+/**
+ * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link SkyblockerMod#scheduler}. Do not instantiate this class.
+ */
public class Scheduler {
private static final Logger LOGGER = LoggerFactory.getLogger(Scheduler.class);
- private int currentTick;
- private final PriorityQueue<ScheduledTask> tasks;
+ private int currentTick = 0;
+ private final PriorityQueue<ScheduledTask> tasks = new PriorityQueue<>();
+ /**
+ * Do not instantiate this class. Use {@link SkyblockerMod#scheduler} instead.
+ */
+ @SuppressWarnings("DeprecatedIsStillUsed")
+ @Deprecated
public Scheduler() {
- currentTick = 0;
- tasks = new PriorityQueue<>();
}
+ /**
+ * Schedules a task to run after a delay.
+ *
+ * @param task the task to run
+ * @param delay the delay in ticks
+ */
public void schedule(Runnable task, int delay) {
- if (delay < 0)
+ if (delay < 0) {
LOGGER.warn("Scheduled a task with negative delay");
- ScheduledTask tmp = new ScheduledTask(currentTick + delay, task);
+ }
+ ScheduledTask tmp = new ScheduledTask(task, currentTick + delay);
tasks.add(tmp);
}
+ /**
+ * Schedules a task to run every period ticks.
+ *
+ * @param task the task to run
+ * @param period the period in ticks
+ */
public void scheduleCyclic(Runnable task, int period) {
- if (period <= 0)
+ if (period <= 0) {
LOGGER.error("Attempted to schedule a cyclic task with period lower than 1");
- else
+ } else {
new CyclicTask(task, period).run();
+ }
+ }
+
+ /**
+ * Schedules a screen to open in the next tick. Used in commands to avoid screen immediately closing after the command is executed.
+ *
+ * @param screenSupplier the supplier of the screen to open
+ */
+ public void queueOpenScreen(Supplier<Screen> screenSupplier) {
+ queueOpenScreen(screenSupplier.get());
+ }
+
+ /**
+ * Schedules a screen to open in the next tick. Used in commands to avoid screen immediately closing after the command is executed.
+ *
+ * @param screen the supplier of the screen to open
+ */
+ public void queueOpenScreen(Screen screen) {
+ MinecraftClient.getInstance().send(() -> MinecraftClient.getInstance().setScreen(screen));
}
public void tick() {
currentTick += 1;
ScheduledTask task;
- while ((task = tasks.peek()) != null && task.schedule <= currentTick) {
+ while ((task = tasks.peek()) != null && task.schedule <= currentTick && runTask(task)) {
tasks.poll();
- task.run();
}
}
- private class CyclicTask implements Runnable {
- private final Runnable inner;
- private final int period;
-
- public CyclicTask(Runnable task, int period) {
- this.inner = task;
- this.period = period;
- }
+ /**
+ * Runs the task if able.
+ *
+ * @param task the task to run
+ * @return {@code true} if the task is run, and {@link false} if task is not run.
+ */
+ protected boolean runTask(Runnable task) {
+ task.run();
+ return true;
+ }
+ /**
+ * A task that runs every period ticks. More specifically, this task reschedules itself to run again after period ticks every time it runs.
+ *
+ * @param inner the task to run
+ * @param period the period in ticks
+ */
+ protected record CyclicTask(Runnable inner, int period) implements Runnable {
@Override
public void run() {
- schedule(this, period);
+ SkyblockerMod.getInstance().scheduler.schedule(this, period);
inner.run();
}
}
- private record ScheduledTask(int schedule, Runnable inner) implements Comparable<ScheduledTask>, Runnable {
+ /**
+ * A task that runs at a specific tick, relative to {@link #currentTick}.
+ *
+ * @param inner the task to run
+ * @param schedule the tick to run at
+ */
+ protected record ScheduledTask(Runnable inner, int schedule) implements Comparable<ScheduledTask>, Runnable {
@Override
public int compareTo(ScheduledTask o) {
return schedule - o.schedule;
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java
new file mode 100644
index 00000000..6bc09456
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java
@@ -0,0 +1,66 @@
+package me.xmrvizzy.skyblocker.utils;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.decoration.ArmorStandEntity;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraft.scoreboard.ScoreboardObjective;
+import net.minecraft.scoreboard.ScoreboardPlayerScore;
+import net.minecraft.scoreboard.Team;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+//TODO Slayer Packet system that can provide information about the current slayer boss, abstract so that different bosses can have different info
+public class SlayerUtils {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SlayerUtils.class);
+
+ //TODO: Cache this, probably included in Packet system
+ public static List<Entity> getEntityArmorStands(Entity entity) {
+ return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(1F, 2.5F, 1F), x -> x instanceof ArmorStandEntity && x.hasCustomName());
+ }
+
+ //Eventually this should be modified so that if you hit a slayer boss all slayer features will work on that boss.
+ public static Entity getSlayerEntity() {
+ if (MinecraftClient.getInstance().world != null) {
+ for (Entity entity : MinecraftClient.getInstance().world.getEntities()) {
+ //Check if entity is Bloodfiend
+ if (entity.hasCustomName() && entity.getCustomName().getString().contains("Bloodfiend")) {
+ //Grab the players username
+ String username = MinecraftClient.getInstance().getSession().getUsername();
+ //Check all armor stands around the boss
+ for (Entity armorStand : getEntityArmorStands(entity)) {
+ //Check if the display name contains the players username
+ if (armorStand.getDisplayName().getString().contains(username)) {
+ return entity;
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public static boolean isInSlayer() {
+ try {
+ ClientPlayerEntity client = MinecraftClient.getInstance().player;
+ if (client == null) return false;
+ Scoreboard scoreboard = MinecraftClient.getInstance().player.getScoreboard();
+ ScoreboardObjective objective = scoreboard.getObjectiveForSlot(1);
+ for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) {
+ Team team = scoreboard.getPlayerTeam(score.getPlayerName());
+ if (team != null) {
+ String line = team.getPrefix().getString() + team.getSuffix().getString();
+ if (line.contains("Slay the boss!")) {
+ return true;
+ }
+ }
+ }
+ } catch (NullPointerException e) {
+ LOGGER.error("[Skyblocker] Error while checking if player is in slayer", e);
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
index 532de0dd..35dfd368 100644
--- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java
@@ -1,26 +1,121 @@
package me.xmrvizzy.skyblocker.utils;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
+import me.xmrvizzy.skyblocker.skyblock.rift.TheRift;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
+import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
+import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.client.network.PlayerListEntry;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ScoreboardObjective;
import net.minecraft.scoreboard.ScoreboardPlayerScore;
import net.minecraft.scoreboard.Team;
+import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+/**
+ * Utility variables and methods for retrieving Skyblock related information.
+ */
public class Utils {
- public static boolean isOnSkyblock = false;
- public static boolean isInDungeons = false;
- public static boolean isInjected = false;
+ private static final String PROFILE_PREFIX = "Profile: ";
+ private static boolean isOnSkyblock = false;
+ private static boolean isInDungeons = false;
+ private static boolean isInjected = false;
+ /**
+ * The following fields store data returned from /locraw: {@link #profile}, {@link #server}, {@link #gameType}, {@link #locationRaw}, and {@link #map}.
+ */
+ @SuppressWarnings("JavadocDeclaration")
+ private static String profile = "";
+ private static String server = "";
+ private static String gameType = "";
+ private static String locationRaw = "";
+ private static String map = "";
+ private static long clientWorldJoinTime = 0;
+ private static boolean sentLocRaw = false;
+ private static long lastLocRaw = 0;
- public static void sbChecker() {
+ public static boolean isOnSkyblock() {
+ return isOnSkyblock;
+ }
+
+ public static boolean isInDungeons() {
+ return isInDungeons;
+ }
+
+ public static boolean isInTheRift() {
+ return getLocationRaw().equals(TheRift.LOCATION);
+ }
+
+ public static boolean isInjected() {
+ return isInjected;
+ }
+
+ /**
+ * @return the profile parsed from the player list.
+ */
+ public static String getProfile() {
+ return profile;
+ }
+
+ /**
+ * @return the server parsed from /locraw.
+ */
+ public static String getServer() {
+ return server;
+ }
+
+ /**
+ * @return the game type parsed from /locraw.
+ */
+ public static String getGameType() {
+ return gameType;
+ }
+
+ /**
+ * @return the location raw parsed from /locraw.
+ */
+ public static String getLocationRaw() {
+ return locationRaw;
+ }
+
+ /**
+ * @return the map parsed from /locraw.
+ */
+ public static String getMap() {
+ return map;
+ }
+
+ public static void init() {
+ ClientPlayConnectionEvents.JOIN.register(Utils::onClientWorldJoin);
+ ClientReceiveMessageEvents.ALLOW_GAME.register(Utils::onChatMessage);
+ ClientReceiveMessageEvents.GAME_CANCELED.register(Utils::onChatMessage); // Somehow this works even though onChatMessage returns a boolean
+ }
+
+ /**
+ * Updates all the fields stored in this class from the sidebar, player list, and /locraw.
+ */
+ public static void update() {
MinecraftClient client = MinecraftClient.getInstance();
+ updateFromScoreboard(client);
+ updateFromPlayerList(client);
+ updateLocRaw();
+ }
+
+ /**
+ * Updates {@link #isOnSkyblock}, {@link #isInDungeons}, and {@link #isInjected} from the scoreboard.
+ */
+ public static void updateFromScoreboard(MinecraftClient client) {
List<String> sidebar;
if (client.world == null || client.isInSingleplayer() || (sidebar = getSidebar()) == null) {
@@ -37,13 +132,13 @@ public class Utils {
isInjected = true;
ItemTooltipCallback.EVENT.register(PriceInfoTooltip::onInjectTooltip);
}
- SkyblockEvents.JOIN.invoker().onSkyblockJoin();
isOnSkyblock = true;
+ SkyblockEvents.JOIN.invoker().onSkyblockJoin();
}
} else if (isOnSkyblock) {
- SkyblockEvents.LEAVE.invoker().onSkyblockLeave();
isOnSkyblock = false;
isInDungeons = false;
+ SkyblockEvents.LEAVE.invoker().onSkyblockLeave();
}
isInDungeons = isOnSkyblock && string.contains("The Catacombs");
}
@@ -52,12 +147,13 @@ public class Utils {
String location = null;
List<String> sidebarLines = getSidebar();
try {
- if( sidebarLines != null) {
+ if (sidebarLines != null) {
for (String sidebarLine : sidebarLines) {
if (sidebarLine.contains("⏣")) location = sidebarLine;
+ if (sidebarLine.contains("ф")) location = sidebarLine; //Rift
}
if (location == null) location = "Unknown";
- location = location.replace('⏣', ' ').strip();
+ location = location.strip();
}
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
@@ -134,4 +230,74 @@ public class Utils {
return null;
}
}
+
+ private static void updateFromPlayerList(MinecraftClient client) {
+ if (client.getNetworkHandler() == null) {
+ return;
+ }
+ for (PlayerListEntry playerListEntry : client.getNetworkHandler().getPlayerList()) {
+ if (playerListEntry.getDisplayName() == null) {
+ continue;
+ }
+ String name = playerListEntry.getDisplayName().getString();
+ if (name.startsWith(PROFILE_PREFIX)) {
+ profile = name.substring(PROFILE_PREFIX.length());
+ }
+ }
+ }
+
+ public static void onClientWorldJoin(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client) {
+ clientWorldJoinTime = System.currentTimeMillis();
+ resetLocRawInfo();
+ }
+
+ /**
+ * Sends /locraw to the server if the player is on skyblock and on a new island.
+ */
+ private static void updateLocRaw() {
+ if (isOnSkyblock) {
+ long currentTime = System.currentTimeMillis();
+ if (!sentLocRaw && currentTime > clientWorldJoinTime + 1000 && currentTime > lastLocRaw + 15000) {
+ SkyblockerMod.getInstance().messageScheduler.sendMessageAfterCooldown("/locraw");
+ sentLocRaw = true;
+ lastLocRaw = currentTime;
+ }
+ } else {
+ resetLocRawInfo();
+ }
+ }
+
+ /**
+ * Parses the /locraw reply from the server
+ *
+ * @return not display the message in chat is the command is sent by the mod
+ */
+ public static boolean onChatMessage(Text text, boolean overlay) {
+ String message = text.getString();
+ if (message.startsWith("{\"server\":") && message.endsWith("}")) {
+ JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject();
+ if (locRaw.has("server")) {
+ server = locRaw.get("server").getAsString();
+ if (locRaw.has("gameType")) {
+ gameType = locRaw.get("gameType").getAsString();
+ }
+ if (locRaw.has("mode")) {
+ locationRaw = locRaw.get("mode").getAsString();
+ }
+ if (locRaw.has("map")) {
+ map = locRaw.get("map").getAsString();
+ }
+ return !sentLocRaw;
+ }
+ }
+ return true;
+ }
+
+ private static void resetLocRawInfo() {
+ sentLocRaw = false;
+ server = "";
+ gameType = "";
+ locationRaw = "";
+ map = "";
+ }
} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/title/Title.java b/src/main/java/me/xmrvizzy/skyblocker/utils/title/Title.java
new file mode 100644
index 00000000..0a3bc845
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/title/Title.java
@@ -0,0 +1,53 @@
+package me.xmrvizzy.skyblocker.utils.title;
+
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Represents a title used for {@link TitleContainer}.
+ *
+ * @see TitleContainer
+ */
+public class Title {
+ private MutableText text;
+ protected float x = -1;
+ protected float y = -1;
+
+ /**
+ * Constructs a new title with the given translation key and formatting to be applied.
+ *
+ * @param textKey the translation key
+ * @param formatting the formatting to be applied to the text
+ */
+ public Title(String textKey, Formatting formatting) {
+ this(Text.translatable(textKey).formatted(formatting));
+ }
+
+ /**
+ * Constructs a new title with the given {@link MutableText}.
+ * Use {@link Text#literal(String)} or {@link Text#translatable(String)} to create a {@link MutableText}
+ *
+ * @param text the mutable text
+ */
+ public Title(MutableText text) {
+ this.text = text;
+ }
+
+ public MutableText getText() {
+ return text;
+ }
+
+ public void setText(MutableText text) {
+ this.text = text;
+ }
+
+ protected boolean isDefaultPos() {
+ return x == -1 && y == -1;
+ }
+
+ protected void resetPos() {
+ this.x = -1;
+ this.y = -1;
+ }
+}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainer.java b/src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainer.java
new file mode 100644
index 00000000..a4e445ee
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainer.java
@@ -0,0 +1,179 @@
+package me.xmrvizzy.skyblocker.utils.title;
+
+import com.mojang.brigadier.Command;
+import me.xmrvizzy.skyblocker.SkyblockerMod;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.text.Text;
+import net.minecraft.util.math.MathHelper;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class TitleContainer {
+ /**
+ * The set of titles which will be rendered.
+ *
+ * @see #containsTitle(Title)
+ * @see #addTitle(Title)
+ * @see #addTitle(Title, int)
+ * @see #removeTitle(Title)
+ */
+ private static final Set<Title> titles = new LinkedHashSet<>();
+
+ public static void init() {
+ HudRenderCallback.EVENT.register(TitleContainer::render);
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker")
+ .then(ClientCommandManager.literal("hud")
+ .then(ClientCommandManager.literal("titleContainer")
+ .executes(context -> {
+ SkyblockerMod.getInstance().scheduler.queueOpenScreen(new TitleContainerConfigScreen(Text.of("Title Container HUD Config")));
+ return Command.SINGLE_SUCCESS;
+ })))));
+ }
+
+ /**
+ * Returns {@code true} if the title is currently shown.
+ *
+ * @param title the title to check
+ * @return whether the title in currently shown
+ */
+ public static boolean containsTitle(Title title) {
+ return titles.contains(title);
+ }
+
+ /**
+ * Adds a title to be shown
+ *
+ * @param title the title to be shown
+ * @return whether the title is already currently being shown
+ */
+ public static boolean addTitle(Title title) {
+ if (titles.add(title)) {
+ title.resetPos();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Adds a title to be shown for a set number of ticks
+ *
+ * @param title the title to be shown
+ * @param ticks the number of ticks to show the title
+ * @return whether the title is already currently being shown
+ */
+ public static boolean addTitle(Title title, int ticks) {
+ if (addTitle(title)) {
+ SkyblockerMod.getInstance().scheduler.schedule(() -> TitleContainer.removeTitle(title), ticks);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Stops showing a title
+ *
+ * @param title the title to stop showing
+ */
+ public static void removeTitle(Title title) {
+ titles.remove(title);
+ }
+
+ private static void render(DrawContext context, float tickDelta) {
+ render(context, titles, SkyblockerConfig.get().general.titleContainer.x, SkyblockerConfig.get().general.titleContainer.y, tickDelta);
+ }
+
+ protected static void render(DrawContext context, Set<Title> titles, int xPos, int yPos, float tickDelta) {
+ var client = MinecraftClient.getInstance();
+ TextRenderer textRenderer = client.textRenderer;
+
+ // Calculate Scale to use
+ float scale = 3F * (SkyblockerConfig.get().general.titleContainer.titleContainerScale / 100F);
+
+ // Grab direction and alignment values
+ SkyblockerConfig.Direction direction = SkyblockerConfig.get().general.titleContainer.direction;
+ SkyblockerConfig.Alignment alignment = SkyblockerConfig.get().general.titleContainer.alignment;
+ // x/y refer to the starting position for the text
+ // y always starts at yPos
+ float x = 0;
+ float y = yPos;
+
+ //Calculate the width of combined text
+ float width = 0;
+ for (Title title : titles) {
+ width += textRenderer.getWidth(title.getText()) * scale + 10;
+ }
+
+ if (alignment == SkyblockerConfig.Alignment.MIDDLE) {
+ if (direction == SkyblockerConfig.Direction.HORIZONTAL) {
+ //If middle aligned horizontally, start the xPosition at half of the width to the left.
+ x = xPos - (width / 2);
+ } else {
+ //If middle aligned vertically, start at xPos, we will shift each text to the left later
+ x = xPos;
+ }
+ }
+ if (alignment == SkyblockerConfig.Alignment.LEFT || alignment == SkyblockerConfig.Alignment.RIGHT) {
+ //If left or right aligned, start at xPos, we will shift each text later
+ x = xPos;
+ }
+
+ for (Title title : titles) {
+
+ //Calculate which x the text should use
+ float xToUse;
+ if (direction == SkyblockerConfig.Direction.HORIZONTAL) {
+ xToUse = alignment == SkyblockerConfig.Alignment.RIGHT ?
+ x - (textRenderer.getWidth(title.getText()) * scale) : //if right aligned we need the text position to be aligned on the right side.
+ x;
+ } else {
+ xToUse = alignment == SkyblockerConfig.Alignment.MIDDLE ?
+ x - (textRenderer.getWidth(title.getText()) * scale) / 2 : //if middle aligned we need the text position to be aligned in the middle.
+ alignment == SkyblockerConfig.Alignment.RIGHT ?
+ x - (textRenderer.getWidth(title.getText()) * scale) : //if right aligned we need the text position to be aligned on the right side.
+ x;
+ }
+
+ //Start displaying the title at the correct position, not at the default position
+ if (title.isDefaultPos()) {
+ title.x = xToUse;
+ title.y = y;
+ }
+
+ //Lerp the texts x and y variables
+ title.x = MathHelper.lerp(tickDelta * 0.5F, title.x, xToUse);
+ title.y = MathHelper.lerp(tickDelta * 0.5F, title.y, y);
+
+ //Translate the matrix to the texts position and scale
+ context.getMatrices().push();
+ context.getMatrices().translate(title.x, title.y, 200);
+ context.getMatrices().scale(scale, scale, scale);
+
+ //Draw text
+ context.drawTextWithShadow(textRenderer, title.getText(), 0, 0, 0xFFFFFF);
+ context.getMatrices().pop();
+
+ //Calculate the x and y positions for the next title
+ if (direction == SkyblockerConfig.Direction.HORIZONTAL) {
+ if (alignment == SkyblockerConfig.Alignment.MIDDLE || alignment == SkyblockerConfig.Alignment.LEFT) {
+ //Move to the right if middle or left aligned
+ x += textRenderer.getWidth(title.getText()) * scale + 10;
+ }
+
+ if (alignment == SkyblockerConfig.Alignment.RIGHT) {
+ //Move to the left if right aligned
+ x -= textRenderer.getWidth(title.getText()) * scale + 10;
+ }
+ } else {
+ //Y always moves by the same amount if vertical
+ y += textRenderer.fontHeight * scale + 10;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainerConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainerConfigScreen.java
new file mode 100644
index 00000000..e729ea15
--- /dev/null
+++ b/src/main/java/me/xmrvizzy/skyblocker/utils/title/TitleContainerConfigScreen.java
@@ -0,0 +1,164 @@
+package me.xmrvizzy.skyblocker.utils.title;
+
+import me.shedaniel.autoconfig.AutoConfig;
+import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
+import me.xmrvizzy.skyblocker.utils.RenderUtils;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.util.math.Vector2f;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Pair;
+import org.lwjgl.glfw.GLFW;
+
+import java.awt.*;
+import java.util.Set;
+
+public class TitleContainerConfigScreen extends Screen {
+ private final Title example1 = new Title(Text.literal("Test1").formatted(Formatting.RED));
+ private final Title example2 = new Title(Text.literal("Test23").formatted(Formatting.AQUA));
+ private final Title example3 = new Title(Text.literal("Testing1234").formatted(Formatting.DARK_GREEN));
+ private float hudX = SkyblockerConfig.get().general.titleContainer.x;
+ private float hudY = SkyblockerConfig.get().general.titleContainer.y;
+
+ protected TitleContainerConfigScreen(Text title) {
+ super(title);
+ }
+
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ renderBackground(context);
+ TitleContainer.render(context, Set.of(example1, example2, example3), (int) hudX, (int) hudY, delta);
+ SkyblockerConfig.Direction direction = SkyblockerConfig.get().general.titleContainer.direction;
+ SkyblockerConfig.Alignment alignment = SkyblockerConfig.get().general.titleContainer.alignment;
+ context.drawCenteredTextWithShadow(textRenderer, "Press Q/E to change Alignment: " + alignment, width / 2, textRenderer.fontHeight * 2, Color.WHITE.getRGB());
+ context.drawCenteredTextWithShadow(textRenderer, "Press R to change Direction: " + direction, width / 2, textRenderer.fontHeight * 3 + 5, Color.WHITE.getRGB());
+ context.drawCenteredTextWithShadow(textRenderer, "Press +/- to change Scale", width / 2, textRenderer.fontHeight * 4 + 10, Color.WHITE.getRGB());
+ context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, textRenderer.fontHeight * 5 + 15, Color.GRAY.getRGB());
+
+ Pair<Vector2f, Vector2f> boundingBox = getSelectionBoundingBox();
+ int x1 = (int) boundingBox.getLeft().getX();
+ int y1 = (int) boundingBox.getLeft().getY();
+ int x2 = (int) boundingBox.getRight().getX();
+ int y2 = (int) boundingBox.getRight().getY();
+
+ context.drawHorizontalLine(x1, x2, y1, Color.RED.getRGB());
+ context.drawHorizontalLine(x1, x2, y2, Color.RED.getRGB());
+ context.drawVerticalLine(x1, y1, y2, Color.RED.getRGB());
+ context.drawVerticalLine(x2, y1, y2, Color.RED.getRGB());
+ }
+
+ private Pair<Vector2f, Vector2f> getSelectionBoundingBox() {
+ SkyblockerConfig.Alignment alignment = SkyblockerConfig.get().general.titleContainer.alignment;
+
+ float midWidth = getSelectionWidth() / 2F;
+ float x1 = 0;
+ float x2 = 0;
+ float y1 = hudY;
+ float y2 = hudY + getSelectionHeight();
+ switch (alignment) {
+ case RIGHT -> {
+ x1 = hudX - midWidth * 2;
+ x2 = hudX;
+ }
+ case MIDDLE -> {
+ x1 = hudX - midWidth;
+ x2 = hudX + midWidth;
+ }
+ case LEFT -> {
+ x1 = hudX;
+ x2 = hudX + midWidth * 2;
+ }
+ }
+ return new Pair<>(new Vector2f(x1, y1), new Vector2f(x2, y2));
+ }
+
+ private float getSelectionHeight() {
+ float scale = (3F * (SkyblockerConfig.get().general.titleContainer.titleContainerScale / 100F));
+ return SkyblockerConfig.get().general.titleContainer.direction == SkyblockerConfig.Direction.HORIZONTAL ?
+ (textRenderer.fontHeight * scale) :
+ (textRenderer.fontHeight + 10F) * 3F * scale;
+ }
+
+ private float getSelectionWidth() {
+ float scale = (3F * (SkyblockerConfig.get().general.titleContainer.titleContainerScale / 100F));
+ return SkyblockerConfig.get().general.titleContainer.direction == SkyblockerConfig.Direction.HORIZONTAL ?
+ (textRenderer.getWidth("Test1") + 10 + textRenderer.getWidth("Test23") + 10 + textRenderer.getWidth("Testing1234")) * scale :
+ textRenderer.getWidth("Testing1234") * scale;
+ }
+
+ @Override
+ public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
+ float midWidth = getSelectionWidth() / 2;
+ float midHeight = getSelectionHeight() / 2;
+ var alignment = SkyblockerConfig.get().general.titleContainer.alignment;
+
+ Pair<Vector2f, Vector2f> boundingBox = getSelectionBoundingBox();
+ float x1 = boundingBox.getLeft().getX();
+ float y1 = boundingBox.getLeft().getY();
+ float x2 = boundingBox.getRight().getX();
+ float y2 = boundingBox.getRight().getY();
+
+ if (RenderUtils.pointExistsInArea((int) mouseX, (int) mouseY, (int) x1, (int) y1, (int) x2, (int) y2) && button == 0) {
+ hudX = switch (alignment) {
+ case LEFT -> (int) mouseX - midWidth;
+ case MIDDLE -> (int) mouseX;
+ case RIGHT -> (int) mouseX + midWidth;
+ };
+ hudY = (int) (mouseY - midHeight);
+ }
+ return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (button == 1) {
+ hudX = (float) this.width / 2;
+ hudY = this.height * 0.6F;
+ }
+ return super.mouseClicked(mouseX, mouseY, button);
+ }
+
+ @Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ if (keyCode == GLFW.GLFW_KEY_Q) {
+ SkyblockerConfig.Alignment current = SkyblockerConfig.get().general.titleContainer.alignment;
+ SkyblockerConfig.get().general.titleContainer.alignment = switch (current) {
+ case LEFT -> SkyblockerConfig.Alignment.MIDDLE;
+ case MIDDLE -> SkyblockerConfig.Alignment.RIGHT;
+ case RIGHT -> SkyblockerConfig.Alignment.LEFT;
+ };
+ }
+ if (keyCode == GLFW.GLFW_KEY_E) {
+ SkyblockerConfig.Alignment current = SkyblockerConfig.get().general.titleContainer.alignment;
+ SkyblockerConfig.get().general.titleContainer.alignment = switch (current) {
+ case LEFT -> SkyblockerConfig.Alignment.RIGHT;
+ case MIDDLE -> SkyblockerConfig.Alignment.LEFT;
+ case RIGHT -> SkyblockerConfig.Alignment.MIDDLE;
+ };
+ }
+ if (keyCode == GLFW.GLFW_KEY_R) {
+ SkyblockerConfig.Direction current = SkyblockerConfig.get().general.titleContainer.direction;
+ SkyblockerConfig.get().general.titleContainer.direction = switch (current) {
+ case HORIZONTAL -> SkyblockerConfig.Direction.VERTICAL;
+ case VERTICAL -> SkyblockerConfig.Direction.HORIZONTAL;
+ };
+ }
+ if (keyCode == GLFW.GLFW_KEY_EQUAL) {
+ SkyblockerConfig.get().general.titleContainer.titleContainerScale += 10;
+ }
+ if (keyCode == GLFW.GLFW_KEY_MINUS) {
+ SkyblockerConfig.get().general.titleContainer.titleContainerScale -= 10;
+ }
+ return super.keyPressed(keyCode, scanCode, modifiers);
+ }
+
+ @Override
+ public void close() {
+ SkyblockerConfig.get().general.titleContainer.x = (int) hudX;
+ SkyblockerConfig.get().general.titleContainer.y = (int) hudY;
+ AutoConfig.getConfigHolder(SkyblockerConfig.class).save();
+ super.close();
+ }
+}
diff --git a/src/main/resources/assets/skyblocker/lang/de_de.json b/src/main/resources/assets/skyblocker/lang/de_de.json
index 56f0fb14..4a84719c 100644
--- a/src/main/resources/assets/skyblocker/lang/de_de.json
+++ b/src/main/resources/assets/skyblocker/lang/de_de.json
@@ -4,6 +4,10 @@
"text.autoconfig.skyblocker.title": "Skyblocker-Einstellungen",
"text.autoconfig.skyblocker.category.general": "Allgemein",
"text.autoconfig.skyblocker.option.general.bars": "Gesundheits-, Mana-, Verteidigungs- und XP-Balken",
+ "text.autoconfig.skyblocker.option.general.tabHud": "Schöneres Tab-HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "Schöneres Tab-HUD aktivieren",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "Skalierungsfaktor für schöners Tab-HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "Wert in %, relativ zur Skalierung des Vanilla-GUIs",
"text.autoconfig.skyblocker.option.general.bars.enableBars": "Balken aktivieren",
"text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Leere Item-Tooltips in Menüs verstecken",
@@ -29,6 +33,9 @@
"text.autoconfig.skyblocker.option.messages.hideMoltenWave": "Nachricht über Molten Wave ausblenden",
"text.autoconfig.skyblocker.option.messages.hideAds": "Werbung im öffentlichen Chat ausblenden",
"key.wikiLookup": "Wiki-Abfrage",
+ "key.skyblocker.playerTgl": "Tab-HUD auf Spielerliste umstellen",
+ "key.skyblocker.defaultTgl": "Tab-HUD auf Standard umstellen",
+ "key.skyblocker.genericTgl": "Tab-HUD auf allgemeine Infos umstellen",
"text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "Rechts",
"text.autoconfig.skyblocker.option.general.quicknav": "Schnellnavigation",
"text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Schnellnavigation Einschalten"
diff --git a/src/main/resources/assets/skyblocker/lang/en_ca.json b/src/main/resources/assets/skyblocker/lang/en_ca.json
new file mode 100644
index 00000000..73255e03
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/lang/en_ca.json
@@ -0,0 +1,11 @@
+{
+ "text.autoconfig.skyblocker.option.general.bars": "Health, Mana, Defence & XP Bars",
+
+ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Grey out chests that have already been opened.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "Livid Colour",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor": "Enable Livid Colour",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor.@Tooltip": "Send the livid colour in the chat during the Livid boss fight.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText": "Livid Colour Text",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip": "Text which will be sent in the chat during the Livid boss fight. The string \"[color]\" will be replaced with the livid colour.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "Solve Select Coloured"
+}
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index b4e320aa..9dc2a856 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -1,6 +1,9 @@
{
"key.categories.skyblocker": "Skyblocker",
"key.hotbarSlotLock": "Slot Lock (Hotbar)",
+ "key.skyblocker.playerTgl": "Switch tab HUD to player list",
+ "key.skyblocker.defaultTgl": "Switch tab HUD to default view",
+ "key.skyblocker.genericTgl": "Switch tab HUD to general info",
"key.wikiLookup": "Wiki Lookup",
"text.autoconfig.skyblocker.title": "Skyblocker Settings",
@@ -17,11 +20,26 @@
"text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "Mana Bar Position",
"text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "Defence Bar Position",
"text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition": "Experience Bar Position",
+ "text.autoconfig.skyblocker.option.general.experiments": "Experiments Solver",
+ "text.autoconfig.skyblocker.option.general.experiments.enableChronomatronSolver": "Enable Chronomatron Solver",
+ "text.autoconfig.skyblocker.option.general.experiments.enableSuperpairsSolver": "Enable Superpairs Solver",
+ "text.autoconfig.skyblocker.option.general.experiments.enableUltrasequencerSolver": "Enable Ultrasequencer Solver",
+ "text.autoconfig.skyblocker.option.general.acceptReparty": "Auto accept Reparty",
+ "text.autoconfig.skyblocker.option.general.fishing": "Fishing Helper",
+ "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "Enable Fishing Helper",
+ "text.autoconfig.skyblocker.option.general.fairySouls": "Fairy Souls Helper",
+ "text.autoconfig.skyblocker.option.general.fairySouls.enableFairySoulsHelper": "Enable Fairy Souls Helper",
"text.autoconfig.skyblocker.option.general.quicknav": "Quicknav",
"text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Enable Quicknav",
"text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift": "View backpack preview without holding Shift",
+ "text.autoconfig.skyblocker.option.general.tabHud": "Fancy tab HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "Enable fancy tab HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "Scale factor of fancy tab HUD",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "Value in %, relative to your vanilla GUI scale",
"text.autoconfig.skyblocker.option.general.itemTooltip": "Item Tooltip",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "Enable NPC Price",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice": "Enable Motes Price",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice.@Tooltip": "Displays the Motes sell price of an item while in The Rift.",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Enable Avg. BIN Price",
"text.autoconfig.skyblocker.option.general.itemTooltip.avg": "Average Type",
"text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "You can choose how many days of average price to be",
@@ -34,6 +52,13 @@
"text.autoconfig.skyblocker.option.general.hitbox": "Hitboxes",
"text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox": "Enable 1.8 farmland hitbox",
"text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox": "Enable 1.8 lever hitbox",
+ "text.autoconfig.skyblocker.option.general.titleContainer": "Title Container",
+ "text.autoconfig.skyblocker.option.general.titleContainer.@Tooltip": "Used to display multiple titles at once, Example use: Vampire Slayer",
+ "text.autoconfig.skyblocker.option.general.titleContainer.titleContainerScale": "Title Container Scale",
+ "text.autoconfig.skyblocker.option.general.titleContainer.x": "Title Container X Position",
+ "text.autoconfig.skyblocker.option.general.titleContainer.y": "Title Container Y Position",
+ "text.autoconfig.skyblocker.option.general.titleContainer.direction": "Title Container Orientation",
+ "text.autoconfig.skyblocker.option.general.titleContainer.alignment": "Title Container Horizontal Alignment",
"skyblocker.itemTooltip.nullMessage": "§b[§6Skyblocker§b] §cItem price information on tooltip will renew in max 60 seconds. If not, check latest.log",
"skyblocker.itemTooltip.noData": "§cNo Data",
@@ -150,14 +175,24 @@
"text.autoconfig.skyblocker.option.general.itemList.enableItemList": "Enable Item List",
"text.autoconfig.skyblocker.category.locations": "Locations",
+ "text.autoconfig.skyblocker.option.locations.barn": "Barn",
+ "text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker": "Solve Hungry Hiker",
+ "text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter": "Solve Treasure Hunter",
"text.autoconfig.skyblocker.option.locations.dungeons": "Dungeons",
"text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus Helper",
"text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Gray out chests that have already been opened.",
"text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Enable Map",
"text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Map Scaling",
+ "text.autoconfig.skyblocker.option.locations.dungeons.mapX": "Map X",
+ "text.autoconfig.skyblocker.option.locations.dungeons.mapY": "Map Y",
"text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "Solve Three Weirdos Puzzle",
"text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "Solve Blaze Puzzle",
"text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "Solve Trivia Puzzle",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "Livid Color",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor": "Enable Livid Color",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor.@Tooltip": "Send the livid color in the chat during the Livid boss fight.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText": "Livid Color Text",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip": "Text which will be sent in the chat during the Livid boss fight. The string \"[color]\" will be replaced with the livid color.",
"text.autoconfig.skyblocker.option.locations.dungeons.terminals": "Terminal Solvers",
"text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "Solve Select Colored",
"text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "Solve Click In Order",
@@ -168,9 +203,18 @@
"text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "Solve Puzzler Puzzle",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "Dwarven HUD",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled": "Enabled",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style": "Style for HUD",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]": "Simple: Shows name and percentage.",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]": "Fancy: Shows name, percentage, progress bar and an icon.",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]": "Classic: Shows name and percentage in a very simple box.",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground": "Enable Background",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.x": "X",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.y": "Y",
+
+ "text.autoconfig.skyblocker.option.locations.rift": "The Rift",
+ "text.autoconfig.skyblocker.option.locations.rift.mirrorverseWaypoints": "Enable Mirrorverse Waypoints",
+ "text.autoconfig.skyblocker.option.locations.rift.mcGrubberStacks": "McGrubber Stacks",
+ "text.autoconfig.skyblocker.option.locations.rift.mcGrubberStacks.@Tooltip": "Used for calculating Motes sell prices.",
"text.autoconfig.skyblocker.category.messages": "Messages",
"text.autoconfig.skyblocker.option.messages.chatFilterResult.PASS": "Disabled",
@@ -187,6 +231,35 @@
"text.autoconfig.skyblocker.option.messages.hideAutopet": "Hide Autopet Messages",
"text.autoconfig.skyblocker.option.messages.hideMana": "Hide Mana Consumption Messages from Action Bar",
"text.autoconfig.skyblocker.option.messages.hideMana.@Tooltip": "Gives a better experience with FancyBar",
+ "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Hide empty item tooltips in menus",
+ "text.autoconfig.skyblocker.category.slayer": "Slayers",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer": "Vampire Slayer",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableEffigyWaypoints": "Enable Effigy Waypoints",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.compactEffigyWaypoints": "Compact Effigy Waypoints",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency": "Effigy Waypoints Update Frequency (Ticks)",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency.@Tooltip": "The lower the value, the more frequent the updates, which may cause lag.",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableHolyIceIndicator": "Enable Holy Ice Indicator",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceIndicatorTickDelay": "Holy Ice Indicator Delay (Ticks)",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency": "Holy Ice Indicator Update Frequency (Ticks)",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency.@Tooltip": "The lower the value, the more frequent the updates, which may cause lag.",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableHealingMelonIndicator": "Enable Healing Melon Indicator",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.healingMelonHealthThreshold": "Healing Melon Indicator Threshold (Hearts)",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableSteakStakeIndicator": "Enable Steak Stake Indicator",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency": "Steak Stake Indicator Update Frequency (Ticks)",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency.@Tooltip": "The lower the value, the more frequent the updates, which may cause lag.",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableManiaIndicator": "Enable Mania Block Indicator",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency": "Mania Indicator Update Frequency (Ticks)",
+ "text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency.@Tooltip": "The lower the value, the more frequent the updates, which may cause lag.",
- "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Hide empty item tooltips in menus"
+ "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Hide empty item tooltips in menus",
+
+ "skyblocker.updaterepository.failed": "§b[§6Skyblocker§b] §cUpdating local repository failed. Remove files manually and restart game.",
+
+ "skyblocker.fishing.reelNow": "Reel in now!",
+ "skyblocker.rift.healNow": "Heal now!",
+ "skyblocker.rift.iceNow": "Ice now!",
+ "skyblocker.rift.mania": "Mania!",
+ "skyblocker.rift.stakeNow": "Stake now!",
+ "skyblocker.fairySouls.markAllFound": "Marked all fairy souls in the current island as found",
+ "skyblocker.fairySouls.markAllMissing": "Marked all fairy souls in the current island as missing"
}
diff --git a/src/main/resources/assets/skyblocker/lang/es_es.json b/src/main/resources/assets/skyblocker/lang/es_es.json
new file mode 100644
index 00000000..7c5ea062
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/lang/es_es.json
@@ -0,0 +1,148 @@
+{
+ "key.categories.skyblocker": "Skyblocker",
+ "key.hotbarSlotLock": "Bloqueo de Slot (Hotbar)",
+ "key.wikiLookup": "Búsqueda en la Wiki",
+ "text.autoconfig.skyblocker.title": "Ajustes de Skyblocker",
+ "text.autoconfig.skyblocker.category.general": "General",
+ "text.autoconfig.skyblocker.option.general.bars": "Salud, Mana, Defensa & Barra de XP",
+ "text.autoconfig.skyblocker.option.general.bars.enableBars": "Habilitar Barras",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions": "Configurar Posición de Barras",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER1": "Capa 1",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "Capa 2",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "Derecha",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "Deshabilitado",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "Posición de la Barra de Salud",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "Posición de la Barra de Mana",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "Posición de la Barra de Defensa",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition": "Posición de la Barra de Experiencia",
+ "text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift": "Ver la previsualización de la mochila sin sostener Shift",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "Habilitar Precios de NPC",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Habilitar Precios Promedio de BIN",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg": "Tipo Promedio",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "Puedes elegir cuantos días de precio promedio sera",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.ONE_DAY": "Precio de 1 día",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "Precio de 3 días",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.BOTH": "Ambos",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "Habilitar el precio mas bajo de BIN",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "Habilitar los precios de compra/venta del Bazar",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate": "Habilitar Fecha del Museo",
+ "text.autoconfig.skyblocker.option.general.hitbox": "Cajas de Colisión",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox": "Habilitar la Caja de Colisión de la 1.8 para la tierra de cultivo",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox": "Habilitar la Caja de Colisión de la 1.8 para la palanca",
+ "skyblocker.itemTooltip.noData": "§cSin Información",
+ "text.autoconfig.skyblocker.category.richPresence": "Discord Rich Presence",
+ "text.autoconfig.skyblocker.option.richPresence.info": "Información de Skyblock",
+ "text.autoconfig.skyblocker.option.richPresence.info.PURSE": "CARTERA",
+ "text.autoconfig.skyblocker.option.richPresence.info.BITS": "BITS",
+ "text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "LOCALIZACIÓN",
+ "text.autoconfig.skyblocker.option.richPresence.cycleMode": "Ciclar Información de Skyblock",
+ "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "Habilitado",
+ "text.autoconfig.skyblocker.option.richPresence.customMessage": "Mensaje Personalizado",
+ "text.autoconfig.skyblocker.category.quickNav": "Navegación Rápida",
+ "text.autoconfig.skyblocker.option.quickNav.enableQuickNav": "Habilitar Navegación Rápida",
+ "text.autoconfig.skyblocker.option.quickNav.button1": "Botón 1",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button2.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button3": "Botón 3",
+ "text.autoconfig.skyblocker.option.quickNav.button4": "Botón 4",
+ "text.autoconfig.skyblocker.option.quickNav.button5": "Botón 5",
+ "text.autoconfig.skyblocker.option.quickNav.button6": "Botón 6",
+ "text.autoconfig.skyblocker.option.quickNav.button7": "Botón 7",
+ "text.autoconfig.skyblocker.option.messages.hideImplosion": "Ocultar el mensaje de Implosion",
+ "text.autoconfig.skyblocker.option.messages.hideCombo": "Ocultar Mensajes de Combos",
+ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Obscurece los cofres que ya han sido abiertos.",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground": "Habilitar Fondo",
+ "text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Habilitar Navegación Rápida",
+ "text.autoconfig.skyblocker.option.general.quicknav": "Navegación Rápida",
+ "text.autoconfig.skyblocker.option.general.itemTooltip": "Información extra de los objetos",
+ "skyblocker.itemTooltip.nullMessage": "§b[§6Skyblocker§b] §cEl precio en la información en los objetos se actualiza cada 60 segundos. De lo contrario revisa lastest.log",
+ "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "Este valor no importa si estas ciclando",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button1.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button2": "Botón 2",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.messages.hideAds": "Ocultar anuncios del Chat Publico",
+ "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "Ocultar mensajes del Pad de Teletransporte",
+ "text.autoconfig.skyblocker.option.messages.hideAutopet": "Ocultar mensajes del Autopet",
+ "text.autoconfig.skyblocker.option.general.itemList": "Lista de Objetos",
+ "text.autoconfig.skyblocker.option.general.itemList.enableItemList": "Habilitar la Lista de Objetos",
+ "text.autoconfig.skyblocker.category.locations": "Localizaciones",
+ "text.autoconfig.skyblocker.option.locations.dungeons": "Mazmorras",
+ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Ayuda con Croesus",
+ "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Habilitar Mapa",
+ "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Escala del Mapa",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "Resuelve el Acertijo de \"Los Tres Chiflados\"",
+ "text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "Resuelve el Acertijo del Blaze",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "Resuelve el Acertijo de Trivia",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines": "Minas Dwarven",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "Habilitar Combustible del Taladro",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "Resolver Fetchur",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "Interfaz de Dwarven",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled": "Habilitado",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.x": "X",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.y": "Y",
+ "text.autoconfig.skyblocker.category.messages": "Mensajes",
+ "text.autoconfig.skyblocker.option.messages.chatFilterResult.PASS": "Deshabilitado",
+ "text.autoconfig.skyblocker.option.messages.chatFilterResult.FILTER": "Filtro",
+ "text.autoconfig.skyblocker.option.messages.chatFilterResult.ACTION_BAR": "Mover a la barra de acción",
+ "text.autoconfig.skyblocker.option.messages.hideAbility": "Ocultar Mensaje de Enfriamiento (Cooldown)",
+ "text.autoconfig.skyblocker.option.messages.hideHeal": "Ocultar Mensajes de Curación",
+ "text.autoconfig.skyblocker.option.messages.hideAOTE": "Ocultar Mensajes de la AOTE",
+ "text.autoconfig.skyblocker.option.messages.hideMana": "Ocultar los Mensajes del Consumo de Maná de la Barra de Acción",
+ "text.autoconfig.skyblocker.option.messages.hideMana.@Tooltip": "Da una mejor experiencia con FancyBar",
+ "skyblocker.update.update_message": "§b[§6Skyblocker§b] §2¡Hay Una nueva versión disponible!",
+ "skyblocker.update.update_link": " §2§nHas Click Aquí§r",
+ "skyblocker.update.update_message_end": " §aPara conocer mas sobre las nuevas características.",
+ "skyblocker.update.hover_text": "Abrir Modrinth",
+ "text.autoconfig.skyblocker.option.general.enableUpdateNotification": "Notificaciones sobre actualizaciones",
+ "skyblocker.api.got_key": "§b[§6Skyblocker§b] §2¡Se añadió tu API key automáticamente!",
+ "skyblocker.updaterepository.failed": "§b[§6Skyblocker§b] §cLa actualización del repositorio local fallo. Elimina los archivos manualmente y reinicia el juego.",
+ "text.autoconfig.skyblocker.option.quickNav.button11": "Botón 11",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button3.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button4.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button5.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button8": "Botón 8",
+ "text.autoconfig.skyblocker.option.quickNav.button9": "Botón 9",
+ "text.autoconfig.skyblocker.option.quickNav.button10": "Botón 10",
+ "text.autoconfig.skyblocker.option.quickNav.button6.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button7.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button8.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button9.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button10.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button11.uiTitle": "Titulo de la Interfaz",
+ "text.autoconfig.skyblocker.option.quickNav.button12": "Botón 12",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item": "Objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.itemName": "Nombre del objeto",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button12.uiTitle": "Titulo de la Interfaz"
+}
diff --git a/src/main/resources/assets/skyblocker/lang/ja_JP.json b/src/main/resources/assets/skyblocker/lang/ja_jp.json
index 101da288..101da288 100644
--- a/src/main/resources/assets/skyblocker/lang/ja_JP.json
+++ b/src/main/resources/assets/skyblocker/lang/ja_jp.json
diff --git a/src/main/resources/assets/skyblocker/lang/ko_KR.json b/src/main/resources/assets/skyblocker/lang/ko_kr.json
index ac6703e2..ac6703e2 100644
--- a/src/main/resources/assets/skyblocker/lang/ko_KR.json
+++ b/src/main/resources/assets/skyblocker/lang/ko_kr.json
diff --git a/src/main/resources/assets/skyblocker/lang/nb_no.json b/src/main/resources/assets/skyblocker/lang/nb_no.json
new file mode 100644
index 00000000..0f96ebb6
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/lang/nb_no.json
@@ -0,0 +1,164 @@
+{
+ "key.wikiLookup": "Wiki-oppslag",
+ "text.autoconfig.skyblocker.title": "Skyblocker-innstillinger",
+ "text.autoconfig.skyblocker.option.general.bars.enableBars": "Aktiver barer",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions": "Konfigurer barposisjoner",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER1": "Lag 1",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "Lag 2",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "Høyre",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "Deaktivert",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "Helsebar posisjon",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "Mana Bar Posisjon",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "Forsvarsbar posisjon",
+ "text.autoconfig.skyblocker.option.general.quicknav": "Hurtignavigasjon",
+ "text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Aktiver hurtignavigasjon",
+ "text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift": "Vis forhåndsvisning av ryggsekk uten å holde Shift nede",
+ "text.autoconfig.skyblocker.option.general.itemTooltip": "Verktøytips for Gjenstad",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "Aktiver NPC-pris",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Aktiver Avg. BIN Pris",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg": "Gjennomsnittlig type",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.ONE_DAY": "Pris for 1 dag",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "Pris for 3 dager",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.BOTH": "Begge",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "Aktiver laveste BIN-pris",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "Aktiver Bazaar kjøp/salg Pris",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate": "Aktiver museum og dato",
+ "text.autoconfig.skyblocker.option.general.hitbox": "Treffbokser",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox": "Aktiver 1.8 treffboks for avlinger",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox": "Aktiver 1.8 spak Treffboks",
+ "skyblocker.itemTooltip.noData": "§cIngen data",
+ "text.autoconfig.skyblocker.category.richPresence": "Discord-rik tilstedeværelse",
+ "text.autoconfig.skyblocker.option.richPresence.info": "Skyblock-informasjon",
+ "text.autoconfig.skyblocker.option.richPresence.info.PURSE": "VESKE",
+ "text.autoconfig.skyblocker.option.richPresence.info.BITS": "BITS",
+ "text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "PLASSERING",
+ "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "Denne verdien har ikke noe å si hvis du cycling",
+ "text.autoconfig.skyblocker.option.richPresence.cycleMode": "Cycle Skyblock Informasjon",
+ "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "Aktivert",
+ "text.autoconfig.skyblocker.option.richPresence.customMessage": "Egendefinert melding",
+ "text.autoconfig.skyblocker.category.quickNav": "Rask navigering",
+ "text.autoconfig.skyblocker.option.quickNav.enableQuickNav": "Aktiver hurtignavigering",
+ "text.autoconfig.skyblocker.option.quickNav.button1.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.count": "Gjenstand Teller",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button1.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button1.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button2": "Knapp 2",
+ "text.autoconfig.skyblocker.option.quickNav.button2.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.itemName": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.count": "Gjenstand Teller",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button2.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button2.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button3": "Knapp 3",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.count": "Gjenstand Teller",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button3.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button3.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button4": "Knapp 4",
+ "text.autoconfig.skyblocker.option.quickNav.button4.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.count": "Gjenstand Teller",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button4.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button5": "Knapp 5",
+ "text.autoconfig.skyblocker.option.quickNav.button5.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.count": "Gjenstand Teller",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.nbt": "NBT",
+ "key.categories.skyblocker": "SkyBlocker",
+ "key.hotbarSlotLock": "Slot lås (Hotbar)",
+ "text.autoconfig.skyblocker.category.general": "Generelt",
+ "text.autoconfig.skyblocker.option.general.bars": "Helse, mana, forsvar og XP-barer",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition": "Experience Bar stilling",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "Du kan velge hvor mange dager med gjennomsnittspris som skal være",
+ "skyblocker.itemTooltip.nullMessage": "§b[§6Skyblocker§b] §cVareprisinformasjon på verktøytips fornyes om maks 60 sekunder. Hvis ikke, sjekk latest.log",
+ "text.autoconfig.skyblocker.option.quickNav.button1": "Knapp 1",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.itemName": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button3.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button4.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button6.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button8.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button10.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.general.itemList.enableItemList": "Aktiver gjenstad liste",
+ "text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "Løs Blaze-puslespillet",
+ "text.autoconfig.skyblocker.option.quickNav.button5.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button5.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button6": "Knapp 6",
+ "text.autoconfig.skyblocker.option.quickNav.button6.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.itemName": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.count": "Gjenstand mengde",
+ "text.autoconfig.skyblocker.option.quickNav.button6.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button7": "Knapp 7",
+ "text.autoconfig.skyblocker.option.quickNav.button7.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.itemName": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.count": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button7.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button7.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button8": "Knapp 8",
+ "text.autoconfig.skyblocker.option.quickNav.button8.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.itemName": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.count": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button8.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button9": "Knapp 8",
+ "text.autoconfig.skyblocker.option.quickNav.button9.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.count": "Gjenstand navn",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button9.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button9.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button10": "Knapp 1",
+ "text.autoconfig.skyblocker.option.quickNav.button10.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.count": "Gjenstand Mengde",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button10.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button11": "Knapp 1",
+ "text.autoconfig.skyblocker.option.quickNav.button11.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item": "Gjenstand",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.count": "Gjenstand Mengde",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button11.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button11.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.quickNav.button12": "Knapp 1",
+ "text.autoconfig.skyblocker.option.quickNav.button12.render": "Rendering",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.itemName": "Gjenstand Navn",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.count": "Gjenstand Mengde",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.nbt": "NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button12.uiTitle": "UI Tittel",
+ "text.autoconfig.skyblocker.option.quickNav.button12.clickEvent": "Klikk hendelse",
+ "text.autoconfig.skyblocker.option.general.itemList": "Gjenstand liste",
+ "text.autoconfig.skyblocker.option.locations.dungeons": "Dungeons",
+ "text.autoconfig.skyblocker.category.locations": "Lokasjoner",
+ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus Hjelper",
+ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "Grå ut kister som allerede er åpnet.",
+ "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Aktiver kart",
+ "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "Kart skalering",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "Løs Three Weirdos-puslespillet",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "Solve the Blaze puzzle",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals": "Terminalløser",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "Løsning Velg farget",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "Løs klikk i rekkefølge",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "Løsning starter med",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines": "Dverggruver",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "Aktiver Drill Fuel",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "Løse Fetchur",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "Løs Puzzler Puzzle",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "Dverg-HUD"
+}
diff --git a/src/main/resources/assets/skyblocker/lang/nn_no.json b/src/main/resources/assets/skyblocker/lang/nn_no.json
new file mode 100644
index 00000000..cd7ed912
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/lang/nn_no.json
@@ -0,0 +1,16 @@
+{
+ "key.hotbarSlotLock": "Slotslås (Hotbar)",
+ "key.wikiLookup": "Wiki Opslag",
+ "text.autoconfig.skyblocker.title": "Skyblocker Innstillinger",
+ "text.autoconfig.skyblocker.category.general": "Generelt",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER1": "Lag 1",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "Lag 2",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "Høgre",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "Deaktivert",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "Helsebar posisjon",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "Mana Bar-posisjon",
+ "key.categories.skyblocker": "SkyBlocker",
+ "text.autoconfig.skyblocker.option.general.bars": "Liv, mana, forsvar og XP-bar",
+ "text.autoconfig.skyblocker.option.general.bars.enableBars": "Aktiver bar",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions": "Konfigurer barposisjoner"
+}
diff --git a/src/main/resources/assets/skyblocker/lang/ru_ru.json b/src/main/resources/assets/skyblocker/lang/ru_ru.json
index 6fd6d329..e399eb62 100644
--- a/src/main/resources/assets/skyblocker/lang/ru_ru.json
+++ b/src/main/resources/assets/skyblocker/lang/ru_ru.json
@@ -1,55 +1,90 @@
{
- "key.categories.skyblocker": "Skyblocker",
- "key.hotbarSlotLock": "Блокировка слотов (В хотбаре)",
- "text.autoconfig.skyblocker.title": "Настройки Skyblocker",
-
- "text.autoconfig.skyblocker.category.general": "Основные",
- "text.autoconfig.skyblocker.option.general.bars": "Полоски здоровья, маны, защиты и опыта",
- "text.autoconfig.skyblocker.option.general.bars.enableBars": "Включить полоски",
-
- "text.autoconfig.skyblocker.category.locations": "Локации",
- "text.autoconfig.skyblocker.option.locations.dungeons": "Катакомбы",
- "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Включить карту",
- "text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "Решать головоломку \"Три чудака\"",
- "text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "Решать головоломку с ифритами",
- "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "Решать головоломку с вопросами",
- "text.autoconfig.skyblocker.option.locations.dwarvenMines": "Гномьи шахты",
- "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "Показывать топливо дрели",
- "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "Решать загадку Fetchur-а",
- "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "Решать загадку Puzzler-а",
-
- "text.autoconfig.skyblocker.category.messages": "Сообщения",
- "text.autoconfig.skyblocker.option.messages.hideAbility": "Скрывать перезарядку способностей",
- "text.autoconfig.skyblocker.option.messages.hideHeal": "Скрывать сообщения об исцелении",
- "text.autoconfig.skyblocker.option.messages.hideAOTE": "Скрывать сообщения AOTE/AOTV",
- "text.autoconfig.skyblocker.option.messages.hideImplosion": "Скрывать сообщения Implosion",
- "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "Скрывать сообщения Molten Wave",
- "text.autoconfig.skyblocker.option.messages.hideAds": "Скрывать рекламу в чате",
- "text.autoconfig.skyblocker.option.locations.dungeons.oldLevers": "Хитбокс рычагов из 1.8",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals": "Авто-решение терминалов",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "Решать выбор по цвету",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "Решать клик по возрастанию",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "Решать начинается с",
- "skyblocker.itemTooltip.nullMessage": "§b[§6Skyblocker§b] §cИнформация о цене предмета обновится через менее чем 60 секунд. Если нет, проверьте latest.log.",
- "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "Скрывать сообщения телепортаторов",
- "text.autoconfig.skyblocker.option.general.quicknav": "Кнопки быстрого доступа",
- "text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Включено",
- "text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Показать средние цены на BIN",
- "text.autoconfig.skyblocker.option.general.itemTooltip.avg": "Период времени (для средней цены)",
- "text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "Можно выбрать, за сколько дней будет браться средняя цена",
- "text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "Показать минимальную цену на BIN",
- "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "Показать цены на Bazaar-е",
- "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate": "Показать дату получения",
- "text.autoconfig.skyblocker.category.richPresence": "Активность в Discord",
- "text.autoconfig.skyblocker.option.richPresence.info": "Отображаемая информация",
- "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "Не имеет значения при цикличном отбражении",
- "text.autoconfig.skyblocker.option.richPresence.cycleMode": "Отображать все опции циклично",
- "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "Включено",
- "text.autoconfig.skyblocker.option.richPresence.customMessage": "Своё сообщение",
- "text.autoconfig.skyblocker.option.general.itemList": "Список предметов",
- "text.autoconfig.skyblocker.option.general.itemList.enableItemList": "Включено",
- "text.autoconfig.skyblocker.option.messages.hideCombo": "Скрывать сообщения о комбо",
- "key.wikiLookup": "Быстрый переход на вики",
- "text.autoconfig.skyblocker.option.general.itemTooltip": "Описание предметов",
- "text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "Показать цены у NPC"
-} \ No newline at end of file
+ "key.categories.skyblocker": "Skyblocker",
+ "key.hotbarSlotLock": "Блокировка слотов (В хотбаре)",
+ "text.autoconfig.skyblocker.title": "Настройки Skyblocker",
+ "text.autoconfig.skyblocker.category.general": "Основные",
+ "text.autoconfig.skyblocker.option.general.bars": "Полоски здоровья, маны, защиты и опыта",
+ "text.autoconfig.skyblocker.option.general.bars.enableBars": "Включить полоски",
+ "text.autoconfig.skyblocker.category.locations": "Локации",
+ "text.autoconfig.skyblocker.option.locations.dungeons": "Катакомбы",
+ "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "Включить карту",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "Решать головоломку \"Три чудака\"",
+ "text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "Решать головоломку с ифритами",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "Решать головоломку с вопросами",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines": "Гномьи шахты",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "Показывать топливо дрели",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "Решать загадку Fetchur-а",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "Решать загадку Puzzler-а",
+ "text.autoconfig.skyblocker.category.messages": "Сообщения",
+ "text.autoconfig.skyblocker.option.messages.hideAbility": "Скрывать сообщения о перезарядке способностей",
+ "text.autoconfig.skyblocker.option.messages.hideHeal": "Скрывать сообщения об исцелении",
+ "text.autoconfig.skyblocker.option.messages.hideAOTE": "Скрывать сообщения AOTE/AOTV",
+ "text.autoconfig.skyblocker.option.messages.hideImplosion": "Скрывать сообщения Implosion",
+ "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "Скрывать сообщения Molten Wave",
+ "text.autoconfig.skyblocker.option.messages.hideAds": "Скрывать рекламу в чате",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals": "Авто-решение терминалов",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "Решать выбор по цвету",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "Решать клик по возрастанию",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "Решать начинается с",
+ "skyblocker.itemTooltip.nullMessage": "§b[§6Skyblocker§b] §cИнформация о цене предмета обновится менее чем через 60 секунд. Если нет, проверьте latest.log",
+ "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "Скрывать сообщения телепортаторов",
+ "skyblocker.update.update_link": " §2§nНажми,§r",
+ "text.autoconfig.skyblocker.option.general.quicknav": "Кнопки быстрого доступа",
+ "text.autoconfig.skyblocker.option.general.quicknav.enableQuicknav": "Включено",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Показать средние цены на BIN",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg": "Период времени (для средней цены)",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "Можно выбрать, за сколько дней будет браться средняя цена",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "Показать минимальную цену на BIN",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "Показать цены на Bazaar-е",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate": "Показать дату получения",
+ "text.autoconfig.skyblocker.category.richPresence": "Активность в статусе Discord",
+ "text.autoconfig.skyblocker.option.richPresence.info": "Отображаемая информация",
+ "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "Не имеет значения при цикличном отбражении",
+ "text.autoconfig.skyblocker.option.richPresence.cycleMode": "Отображать все опции циклично",
+ "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "Включено",
+ "text.autoconfig.skyblocker.option.richPresence.customMessage": "Своё сообщение",
+ "text.autoconfig.skyblocker.option.general.itemList": "Список предметов",
+ "text.autoconfig.skyblocker.option.general.itemList.enableItemList": "Включено",
+ "text.autoconfig.skyblocker.option.messages.hideCombo": "Скрывать сообщения о комбо",
+ "key.wikiLookup": "Быстрый переход на вики",
+ "text.autoconfig.skyblocker.option.general.itemTooltip": "Описание предмета",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "Показать цены у NPC",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions": "Изменить расположение полосок",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER1": "Слой 1",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "Слой 2",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "Цена за 3 дня",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox": "Использовать хитбокс пашни из 1.8",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "Расположение полоски здоровья",
+ "text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift": "Просматривать содержимое рюкзаков без удержания кнопки Shift",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "Отключить",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "Расположение полоски маны",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item": "Предмет",
+ "skyblocker.fishing.reelNow": "Лови сейчас же!",
+ "text.autoconfig.skyblocker.option.messages.hideAutopet": "Скрывать сообщения Autopet",
+ "text.autoconfig.skyblocker.option.messages.hideMana": "Скрывать сообщения о расходе маны из Action Bar",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "Справа",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "Расположение полоски защиты",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition": "Расположение полоски опыта",
+ "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Скрывать описания предметов в меню",
+ "text.autoconfig.skyblocker.option.general.fishing": "Помощь в рыбалке",
+ "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "Включить помощь в рыбалке",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.BOTH": "Обе",
+ "text.autoconfig.skyblocker.option.general.hitbox": "Хитбоксы",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox": "Использовать хитбокс рычагов из 1.8",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.ONE_DAY": "Цена за 1 день",
+ "skyblocker.itemTooltip.noData": "§cНет данных",
+ "text.autoconfig.skyblocker.option.richPresence.info.PURSE": "Кошелек",
+ "text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "Локация",
+ "text.autoconfig.skyblocker.category.quickNav": "Быстрый доступ",
+ "text.autoconfig.skyblocker.option.quickNav.enableQuickNav": "Включить быстрый доступ",
+ "text.autoconfig.skyblocker.option.quickNav.button1": "Кнопка 1",
+ "text.autoconfig.skyblocker.option.quickNav.button2": "Кнопка 2",
+ "text.autoconfig.skyblocker.option.quickNav.button3": "Кнопка 3",
+ "text.autoconfig.skyblocker.option.general.tabHud": "Красивое TAB меню",
+ "key.skyblocker.defaultTgl": "Показывать стандартное меню TAB",
+ "key.skyblocker.playerTgl": "Показывать список игроков в меню TAB",
+ "key.skyblocker.genericTgl": "Показывать общую информацию в меню TAB",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled": "Включить красивое TAB меню",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale": "Размер TAB меню",
+ "text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip": "Значение в %, по отношению к размеру интерфейса игры"
+}
diff --git a/src/main/resources/assets/skyblocker/lang/tr_tr.json b/src/main/resources/assets/skyblocker/lang/tr_tr.json
new file mode 100644
index 00000000..1b07aff8
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/lang/tr_tr.json
@@ -0,0 +1,71 @@
+{
+ "text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift": "Shift'e basmadan sırt çantası ön izlemesini görüntüleyin",
+ "text.autoconfig.skyblocker.option.general.itemTooltip": "Eşya Açıklamaları",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice": "NPC fiyatını göster",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "Ort. BIN fiyatını göster",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg": "Ortalama Türü",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.ONE_DAY": "1 günlük",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "3 günlük",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.BOTH": "İkisi de",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "Pazar alış/satış fiyatını göster",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate": "Müze ve tarih bilgisini göster",
+ "text.autoconfig.skyblocker.option.general.hitbox": "Hitbox'lar",
+ "skyblocker.itemTooltip.nullMessage": "§b[§6Skyblocker§b] §cEşya açıklamasındaki ürün fiyat bilgisi en fazla 60 saniye içinde yenilenecektir. Aksi takdirde latest.log dosyasını kontrol edin",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox": "1.8 tarım hitboxlarını etkinleştir",
+ "text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox": "1.8 şalter hitboxunu etkinleştir",
+ "skyblocker.itemTooltip.noData": "§cVeri yok",
+ "text.autoconfig.skyblocker.category.richPresence": "Discord Özel Durumu",
+ "text.autoconfig.skyblocker.option.richPresence.info": "Skyblock bilgisi",
+ "text.autoconfig.skyblocker.option.richPresence.info.PURSE": "Cüzdan",
+ "text.autoconfig.skyblocker.option.richPresence.info.BITS": "Bit",
+ "text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "Konum",
+ "text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "Eğer döngüyü açtıysanız bu değer geçersiz kalır",
+ "text.autoconfig.skyblocker.option.richPresence.cycleMode": "Skyblock bilgilerini döngüye al",
+ "text.autoconfig.skyblocker.option.richPresence.customMessage": "Özel mesaj",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud": "Dwarven HUD",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "Fetchur'un mesajını çöz",
+ "skyblocker.fishing.reelNow": "Şimdi sarıl!",
+ "text.autoconfig.skyblocker.option.messages.chatFilterResult.PASS": "Devre dışı",
+ "text.autoconfig.skyblocker.option.messages.chatFilterResult.FILTER": "Filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideAbility": "Yetenek bekleme süresini filtrele",
+ "text.autoconfig.skyblocker.option.messages.chatFilterResult.ACTION_BAR": "Aksiyon barına taşı",
+ "text.autoconfig.skyblocker.option.messages.hideHeal": "İyileştirme mesajlarını filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideAOTE": "AOTE mesajlarını filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "Molten Wave mesajını filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideAds": "Genel sohbetteki reklamları filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "Teleport Pad mesajlarını filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideCombo": "Kombo mesajlarını filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideAutopet": "Autopet mesajlarını filtrele",
+ "text.autoconfig.skyblocker.option.messages.hideMana": "Aksiyon barındaki mana tüketimlerini gizle",
+ "text.autoconfig.skyblocker.option.messages.hideMana.@Tooltip": "FancyBar ile daha iyi bir deneyim sunar",
+ "text.autoconfig.skyblocker.option.general.enableUpdateNotification": "Güncelleme Bildirimleri",
+ "skyblocker.api.got_key": "§b[§6Skyblocker§b] §2API anahtarınız otomatik olarak kaydedildi!",
+ "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "Menülerdeki boş eşya açıklamalarını gizle",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.y": "Y",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.x": "X",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground": "Arka planı göster",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled": "Etkinleştir",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "Kaç günlük ortalamanın gösterileceğini seçebilirsiniz",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "En düşük BIN fiyatını göster",
+ "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "Aktif",
+ "skyblocker.update.update_message": "§b[§6Skyblocker§b] §2Yeni bir sürüm mevcut!",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "Puzzler'ın bulmacasını çöz",
+ "key.hotbarSlotLock": "Slot Kilidi (Araç Çubuğu)",
+ "key.wikiLookup": "Wiki Araması",
+ "text.autoconfig.skyblocker.title": "Skyblocker Ayarları",
+ "text.autoconfig.skyblocker.category.general": "Genel",
+ "text.autoconfig.skyblocker.option.general.bars": "Can, Mana, Defans ve XP Barları",
+ "text.autoconfig.skyblocker.option.general.bars.enableBars": "Barları Etkinleştir",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions": "Bar Konumları",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "Devre dışı",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "Can barı konumu",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "Mana barı konumu",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "Defans barı konumu",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition": "Tecrübe barı konumu",
+ "key.categories.skyblocker": "Skyblocker",
+ "skyblocker.updaterepository.failed": "§b[§6Skyblocker§b] §cYerel depo güncellenemedi. Dosyaları manuel olarak silip oyunu tekrar başlatın.",
+ "text.autoconfig.skyblocker.option.general.fishing": "Balık Tutma Yardımcısı",
+ "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "Balık tutma yardımcısını aktifleştir",
+ "text.autoconfig.skyblocker.category.messages": "Mesajlar",
+ "text.autoconfig.skyblocker.option.messages.hideImplosion": "Implosion mesajını filtrele"
+}
diff --git a/src/main/resources/assets/skyblocker/lang/zh_cn.json b/src/main/resources/assets/skyblocker/lang/zh_cn.json
index 78815a12..2708c250 100644
--- a/src/main/resources/assets/skyblocker/lang/zh_cn.json
+++ b/src/main/resources/assets/skyblocker/lang/zh_cn.json
@@ -9,7 +9,7 @@
"text.autoconfig.skyblocker.option.general.bars.barpositions": "配置属性条位置",
"text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER1": "下排",
"text.autoconfig.skyblocker.option.general.bars.barpositions.LAYER2": "上排",
- "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "右侧",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "快捷栏右侧",
"text.autoconfig.skyblocker.option.general.bars.barpositions.NONE": "禁用",
"text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "生命条位置",
"text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "法力条位置",
@@ -23,8 +23,8 @@
"text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN": "显示平均BIN(立即购买)价格",
"text.autoconfig.skyblocker.option.general.itemTooltip.avg": "平均类型",
"text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip": "你可以选择查看多少天的平均价格",
- "text.autoconfig.skyblocker.option.general.itemTooltip.avg.ONE_DAY": "1天价格",
- "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "3天价格",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.ONE_DAY": "一日内均价",
+ "text.autoconfig.skyblocker.option.general.itemTooltip.avg.THREE_DAY": "三日内均价",
"text.autoconfig.skyblocker.option.general.itemTooltip.avg.BOTH": "同时显示",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN": "显示最低BIN(立即购买)价格",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice": "显示集市购买/卖出价格",
@@ -37,41 +37,44 @@
"text.autoconfig.skyblocker.category.richPresence": "Discord活动状态",
"text.autoconfig.skyblocker.option.richPresence.info": "Skyblock信息",
"text.autoconfig.skyblocker.option.richPresence.info.PURSE": "钱包",
- "text.autoconfig.skyblocker.option.richPresence.info.BITS": "比特",
+ "text.autoconfig.skyblocker.option.richPresence.info.BITS": "点券",
"text.autoconfig.skyblocker.option.richPresence.info.LOCATION": "位置",
"text.autoconfig.skyblocker.option.richPresence.info.@Tooltip": "如果您正在循环模式,这个值将不会生效",
"text.autoconfig.skyblocker.option.richPresence.cycleMode": "循环Skyblock信息",
- "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "启用",
+ "text.autoconfig.skyblocker.option.richPresence.enableRichPresence": "已启用",
"text.autoconfig.skyblocker.option.richPresence.customMessage": "自定义消息",
"text.autoconfig.skyblocker.category.quickNav": "快速导航",
"text.autoconfig.skyblocker.option.quickNav.enableQuickNav": "启用快速导航",
- "text.autoconfig.skyblocker.option.quickNav.button1": "按钮1",
- "text.autoconfig.skyblocker.option.quickNav.button2": "按钮2",
- "text.autoconfig.skyblocker.option.quickNav.button3": "按钮3",
- "text.autoconfig.skyblocker.option.quickNav.button4": "按钮4",
- "text.autoconfig.skyblocker.option.quickNav.button5": "按钮5",
- "text.autoconfig.skyblocker.option.quickNav.button6": "按钮6",
- "text.autoconfig.skyblocker.option.quickNav.button7": "按钮7",
- "text.autoconfig.skyblocker.option.quickNav.button8": "按钮8",
- "text.autoconfig.skyblocker.option.quickNav.button9": "按钮9",
- "text.autoconfig.skyblocker.option.quickNav.button10": "按钮10",
- "text.autoconfig.skyblocker.option.quickNav.button11": "按钮11",
- "text.autoconfig.skyblocker.option.quickNav.button12": "按钮12",
+ "text.autoconfig.skyblocker.option.quickNav.button1": "热键1",
+ "text.autoconfig.skyblocker.option.quickNav.button2": "热键2",
+ "text.autoconfig.skyblocker.option.quickNav.button3": "热键3",
+ "text.autoconfig.skyblocker.option.quickNav.button4": "热键4",
+ "text.autoconfig.skyblocker.option.quickNav.button5": "热键5",
+ "text.autoconfig.skyblocker.option.quickNav.button6": "热键6",
+ "text.autoconfig.skyblocker.option.quickNav.button7": "热键7",
+ "text.autoconfig.skyblocker.option.quickNav.button8": "热键8",
+ "text.autoconfig.skyblocker.option.quickNav.button9": "热键9",
+ "text.autoconfig.skyblocker.option.quickNav.button10": "热键10",
+ "text.autoconfig.skyblocker.option.quickNav.button11": "热键11",
+ "text.autoconfig.skyblocker.option.quickNav.button12": "热键12",
"text.autoconfig.skyblocker.option.general.itemList": "物品列表",
"text.autoconfig.skyblocker.option.general.itemList.enableItemList": "启用物品列表",
"text.autoconfig.skyblocker.category.locations": "位置",
+ "text.autoconfig.skyblocker.option.locations.barn": "农业岛屿",
+ "text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker": "Hungry Hikers 所需物品提示",
+ "text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter": "Treasure Hunter 助手",
"text.autoconfig.skyblocker.option.locations.dungeons": "地牢",
"text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper": "Croesus助手",
- "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "用灰色显示出已经打开过的箱子",
- "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "启用地图",
+ "text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip": "将打开过的箱子标记为灰色",
+ "text.autoconfig.skyblocker.option.locations.dungeons.enableMap": "启用地牢地图",
"text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos": "解决三怪人迷题",
- "text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "解决烈焰人迷题",
- "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "解决常识迷题",
+ "text.autoconfig.skyblocker.option.locations.dungeons.blazesolver": "烈焰人迷题助手",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia": "常识问答谜题助手",
"text.autoconfig.skyblocker.option.locations.dungeons.terminals": "终端助手",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "解决颜色迷题",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "解决排序迷题",
- "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "解决开头字母迷题",
- "text.autoconfig.skyblocker.option.locations.dwarvenMines": "矮人矿道",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor": "选色终端助手",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder": "排序终端助手",
+ "text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith": "首字母谜题终端助手",
+ "text.autoconfig.skyblocker.option.locations.dwarvenMines": "矮人矿井",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel": "显示钻头燃料",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur": "解决Fetchur的迷题",
"text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler": "解决Puzzler的迷题",
@@ -86,14 +89,111 @@
"text.autoconfig.skyblocker.option.messages.chatFilterResult.ACTION_BAR": "移动到动作栏",
"text.autoconfig.skyblocker.option.messages.hideAbility": "隐藏技能冷却",
"text.autoconfig.skyblocker.option.messages.hideHeal": "隐藏治疗消息",
- "text.autoconfig.skyblocker.option.messages.hideAOTE": "隐藏AOTE消息",
- "text.autoconfig.skyblocker.option.messages.hideImplosion": "隐藏内爆技能消息",
- "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "隐藏熔融波技能消息",
+ "text.autoconfig.skyblocker.option.messages.hideAOTE": "隐藏瞬息之刃的提示消息",
+ "text.autoconfig.skyblocker.option.messages.hideImplosion": "隐藏核心爆裂技能的提示消息",
+ "text.autoconfig.skyblocker.option.messages.hideMoltenWave": "隐藏 Molten Wave 技能的提示消息",
"text.autoconfig.skyblocker.option.messages.hideAds": "从公屏聊天中隐藏广告",
"text.autoconfig.skyblocker.option.messages.hideTeleportPad": "隐藏传送点消息",
"text.autoconfig.skyblocker.option.messages.hideCombo": "隐藏连杀消息",
"text.autoconfig.skyblocker.option.messages.hideAutopet": "隐藏自动宠物消息",
- "text.autoconfig.skyblocker.option.messages.hideMana": "在动作栏中隐藏魔力消耗信息",
- "text.autoconfig.skyblocker.option.messages.hideMana.@Tooltip": "被已经提供了更好方案的属性条代替",
- "text.autoconfig.skyblocker.option.quickNav.button1.item.nbt": "NBT"
+ "text.autoconfig.skyblocker.option.messages.hideMana": "在动作栏中隐藏法力消耗信息",
+ "text.autoconfig.skyblocker.option.messages.hideMana.@Tooltip": "已被更好的属性条代替",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "隐藏菜单中分隔符的物品信息",
+ "text.autoconfig.skyblocker.option.locations.dungeons.mapScaling": "地图界面大小",
+ "skyblocker.updaterepository.failed": "§b[§6Skyblocker§b] §c更新本地数据存储库失败,请手动删库并重启游戏",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button1.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button1.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button1.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button2.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button1.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button2.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button2.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button2.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button3.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button3.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button3.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button4.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button4.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button4.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button5.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button5.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button5.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button5.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button6.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button6.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button6.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button7.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.count": "热键物品显示数量",
+ "text.autoconfig.skyblocker.option.quickNav.button7.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button7.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button8.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.count": "热键物品显示数量",
+ "text.autoconfig.skyblocker.option.quickNav.button8.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button8.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button8.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button9.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button9.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button9.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button10.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button10.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button10.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button12.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button11.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button11.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button11.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button11.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button12.render": "是否显示该热键",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button12.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button12.uiTitle": "快捷界面标题",
+ "text.autoconfig.skyblocker.option.quickNav.button3.item": "热键所显示物品",
+ "text.autoconfig.skyblocker.option.quickNav.button4.item.count": "物品数量",
+ "text.autoconfig.skyblocker.option.quickNav.button6.item.nbt": "物品NBT",
+ "text.autoconfig.skyblocker.option.quickNav.button7.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.quickNav.button9.item.itemName": "物品ID",
+ "text.autoconfig.skyblocker.option.quickNav.button10.clickEvent": "点击时所执行的命令",
+ "text.autoconfig.skyblocker.option.general.fishing": "钓鱼助手",
+ "text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper": "启用钓鱼助手",
+ "skyblocker.fishing.reelNow": "收竿!",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor": "启用真 Livid 的颜色提示",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor": "提示真 Livid 的颜色",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor.@Tooltip": "将真 Livid 的颜色发送到聊天栏",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText": "真 Livid 颜色提示信息",
+ "text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip": "Livid Boss战时发送到聊天栏的信息, 字段 “[color]” 将被替换为真 Livid 的颜色",
+ "key.skyblocker.playerTgl": "将tab键所显示的列表改为玩家列表",
+ "key.skyblocker.defaultTgl": "将tab键所显示的列表改为默认空岛生存列表",
+ "key.skyblocker.genericTgl": "将tab键所显示的列表改为通用信息列表"
}
diff --git a/src/main/resources/assets/skyblocker/lang/zh_tw.json b/src/main/resources/assets/skyblocker/lang/zh_tw.json
new file mode 100644
index 00000000..3613757e
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/lang/zh_tw.json
@@ -0,0 +1,24 @@
+{
+ "text.autoconfig.skyblocker.title": "Skyblocker設定",
+ "text.autoconfig.skyblocker.category.general": "一般設定",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition": "血量條位置",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition": "魔力條位置",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition": "防禦條位置",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition": "經驗条位置",
+ "key.wikiLookup": "查閱Wiki",
+ "key.hotbarSlotLock": "鎖定快捷欄",
+ "key.categories.skyblocker": "Skyblocker",
+ "text.autoconfig.skyblocker.option.messages.hideAOTE": "隱藏終焉之貌的提示訊息",
+ "text.autoconfig.skyblocker.option.messages.hideAds": "从公頻訊息中隱藏廣告",
+ "text.autoconfig.skyblocker.option.messages.hideTeleportPad": "隱藏傳送點訊息",
+ "text.autoconfig.skyblocker.option.messages.hideCombo": "隱藏連殺訊息",
+ "text.autoconfig.skyblocker.option.messages.hideAutopet": "隱藏自動寵物訊息",
+ "skyblocker.update.hover_text": "打開Modrinth",
+ "text.autoconfig.skyblocker.option.general.hideEmptyTooltips": "隱藏選單玻璃物品資訊\"",
+ "skyblocker.api.got_key": "§b[§6Skyblocker§b] §2已自动設定你的API金鑰!",
+ "text.autoconfig.skyblocker.option.general.bars.barpositions.RIGHT": "快捷欄右側",
+ "text.autoconfig.skyblocker.option.messages.hideAbility": "隱藏技能冷卻",
+ "text.autoconfig.skyblocker.option.messages.hideHeal": "隱藏治療訊息",
+ "text.autoconfig.skyblocker.option.messages.hideMana": "在動作欄中隱藏魔力消耗的提示訊息",
+ "text.autoconfig.skyblocker.option.general.bars": "血量,魔力,防御以及經驗計量條"
+}
diff --git a/src/main/resources/assets/skyblocker/mirrorverse_waypoints.json b/src/main/resources/assets/skyblocker/mirrorverse_waypoints.json
new file mode 100644
index 00000000..2bc0296e
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/mirrorverse_waypoints.json
@@ -0,0 +1,1019 @@
+{
+ "sections": [
+ {
+ "name": "Lava Path",
+ "waypoints": [
+ {
+ "x": -101,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -99,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -95,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -95,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -95,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -84,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -91,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -94,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -101,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -94,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -85,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -84,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -105,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -82,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -82,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -104,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -87,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -91,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -96,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -96,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -97,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -83,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -84,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -97,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -92,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -95,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -93,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -96,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -93,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -94,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -84,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -84,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -92,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -85,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -104,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -105,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -91,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -93,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -91,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -101,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -92,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -82,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -87,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -84,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -92,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -96,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -96,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -98,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -96,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -85,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -87,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -103,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -95,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -89,
+ "y": 52,
+ "z": -107
+ },
+ {
+ "x": -92,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -86,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -88,
+ "y": 52,
+ "z": -115
+ },
+ {
+ "x": -92,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -91,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -110
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -93,
+ "y": 52,
+ "z": -116
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -111
+ },
+ {
+ "x": -104,
+ "y": 52,
+ "z": -114
+ },
+ {
+ "x": -83,
+ "y": 52,
+ "z": -112
+ },
+ {
+ "x": -83,
+ "y": 52,
+ "z": -113
+ },
+ {
+ "x": -100,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -102,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -90,
+ "y": 52,
+ "z": -108
+ },
+ {
+ "x": -97,
+ "y": 52,
+ "z": -108
+ }
+ ]
+ },
+ {
+ "name": "Upside Down Parkour",
+ "waypoints": [
+ {
+ "x": -137,
+ "y": 39,
+ "z": -104
+ },
+ {
+ "x": -169,
+ "y": 42,
+ "z": -116
+ },
+ {
+ "x": -173,
+ "y": 43,
+ "z": -112
+ },
+ {
+ "x": -197,
+ "y": 39,
+ "z": -92
+ },
+ {
+ "x": -161,
+ "y": 39,
+ "z": -92
+ },
+ {
+ "x": -137,
+ "y": 40,
+ "z": -116
+ },
+ {
+ "x": -213,
+ "y": 41,
+ "z": -116
+ },
+ {
+ "x": -173,
+ "y": 41,
+ "z": -96
+ },
+ {
+ "x": -205,
+ "y": 41,
+ "z": -100
+ },
+ {
+ "x": -205,
+ "y": 43,
+ "z": -108
+ },
+ {
+ "x": -137,
+ "y": 41,
+ "z": -96
+ },
+ {
+ "x": -145,
+ "y": 40,
+ "z": -104
+ },
+ {
+ "x": -149,
+ "y": 43,
+ "z": -112
+ },
+ {
+ "x": -189,
+ "y": 42,
+ "z": -120
+ },
+ {
+ "x": -157,
+ "y": 41,
+ "z": -104
+ },
+ {
+ "x": -189,
+ "y": 43,
+ "z": -116
+ },
+ {
+ "x": -193,
+ "y": 42,
+ "z": -104
+ },
+ {
+ "x": -161,
+ "y": 40,
+ "z": -120
+ },
+ {
+ "x": -221,
+ "y": 43,
+ "z": -104
+ },
+ {
+ "x": -185,
+ "y": 41,
+ "z": -108
+ },
+ {
+ "x": -141,
+ "y": 41,
+ "z": -120
+ },
+ {
+ "x": -157,
+ "y": 39,
+ "z": -116
+ },
+ {
+ "x": -153,
+ "y": 40,
+ "z": -100
+ },
+ {
+ "x": -209,
+ "y": 43,
+ "z": -120
+ },
+ {
+ "x": -129,
+ "y": 39,
+ "z": -108
+ },
+ {
+ "x": -189,
+ "y": 43,
+ "z": -100
+ },
+ {
+ "x": -141,
+ "y": 42,
+ "z": -100
+ },
+ {
+ "x": -133,
+ "y": 40,
+ "z": -108
+ },
+ {
+ "x": -209,
+ "y": 42,
+ "z": -104
+ },
+ {
+ "x": -173,
+ "y": 41,
+ "z": -100
+ },
+ {
+ "x": -189,
+ "y": 43,
+ "z": -112
+ },
+ {
+ "x": -145,
+ "y": 40,
+ "z": -108
+ },
+ {
+ "x": -217,
+ "y": 42,
+ "z": -108
+ },
+ {
+ "x": -169,
+ "y": 41,
+ "z": -120
+ },
+ {
+ "x": -165,
+ "y": 43,
+ "z": -109
+ },
+ {
+ "x": -145,
+ "y": 42,
+ "z": -116
+ },
+ {
+ "x": -165,
+ "y": 42,
+ "z": -104
+ },
+ {
+ "x": -181,
+ "y": 40,
+ "z": -112
+ },
+ {
+ "x": -193,
+ "y": 41,
+ "z": -108
+ },
+ {
+ "x": -201,
+ "y": 40,
+ "z": -96
+ },
+ {
+ "x": -137,
+ "y": 41,
+ "z": -124
+ },
+ {
+ "x": -173,
+ "y": 43,
+ "z": -108
+ },
+ {
+ "x": -133,
+ "y": 40,
+ "z": -100
+ },
+ {
+ "x": -201,
+ "y": 40,
+ "z": -116
+ },
+ {
+ "x": -185,
+ "y": 41,
+ "z": -108
+ },
+ {
+ "x": -125,
+ "y": 39,
+ "z": -108
+ },
+ {
+ "x": -161,
+ "y": 42,
+ "z": -112
+ },
+ {
+ "x": -165,
+ "y": 40,
+ "z": -96
+ },
+ {
+ "x": -141,
+ "y": 39,
+ "z": -112
+ },
+ {
+ "x": -177,
+ "y": 39,
+ "z": -92
+ },
+ {
+ "x": -213,
+ "y": 41,
+ "z": -112
+ },
+ {
+ "x": -153,
+ "y": 42,
+ "z": -108
+ },
+ {
+ "x": -205,
+ "y": 42,
+ "z": -120
+ },
+ {
+ "x": -197,
+ "y": 43,
+ "z": -112
+ },
+ {
+ "x": -181,
+ "y": 42,
+ "z": -104
+ },
+ {
+ "x": -165,
+ "y": 41,
+ "z": -100
+ },
+ {
+ "x": -193,
+ "y": 42,
+ "z": -96
+ },
+ {
+ "x": -133,
+ "y": 40,
+ "z": -120
+ },
+ {
+ "x": -185,
+ "y": 41,
+ "z": -100
+ },
+ {
+ "x": -165,
+ "y": 41,
+ "z": -124
+ },
+ {
+ "x": -177,
+ "y": 40,
+ "z": -104
+ },
+ {
+ "x": -157,
+ "y": 40,
+ "z": -96
+ },
+ {
+ "x": -205,
+ "y": 41,
+ "z": -116
+ },
+ {
+ "x": -181,
+ "y": 40,
+ "z": -96
+ },
+ {
+ "x": -201,
+ "y": 43,
+ "z": -108
+ },
+ {
+ "x": -185,
+ "y": 41,
+ "z": -116
+ }
+ ]
+ },
+ {
+ "name": "Turbulator Parkour",
+ "waypoints": [
+ {
+ "x": -302,
+ "y": 34,
+ "z": -107
+ },
+ {
+ "x": -304,
+ "y": 52,
+ "z": -109
+ },
+ {
+ "x": -306,
+ "y": 16,
+ "z": -107
+ },
+ {
+ "x": -304,
+ "y": 12,
+ "z": -107
+ },
+ {
+ "x": -308,
+ "y": 6,
+ "z": -105
+ },
+ {
+ "x": -302,
+ "y": 44,
+ "z": -107
+ },
+ {
+ "x": -306,
+ "y": 24,
+ "z": -109
+ },
+ {
+ "x": -302,
+ "y": 18,
+ "z": -111
+ },
+ {
+ "x": -300,
+ "y": 36,
+ "z": -109
+ },
+ {
+ "x": -304,
+ "y": 30,
+ "z": -103
+ },
+ {
+ "x": -302,
+ "y": 26,
+ "z": -111
+ },
+ {
+ "x": -308,
+ "y": 14,
+ "z": -103
+ },
+ {
+ "x": -300,
+ "y": 46,
+ "z": -103
+ },
+ {
+ "x": -300,
+ "y": 10,
+ "z": -111
+ },
+ {
+ "x": -300,
+ "y": 20,
+ "z": -107
+ },
+ {
+ "x": -306,
+ "y": 54,
+ "z": -111
+ },
+ {
+ "x": -306,
+ "y": 4,
+ "z": -103
+ },
+ {
+ "x": -300,
+ "y": 28,
+ "z": -107
+ },
+ {
+ "x": -306,
+ "y": 42,
+ "z": -111
+ },
+ {
+ "x": -308,
+ "y": 50,
+ "z": -105
+ },
+ {
+ "x": -304,
+ "y": 48,
+ "z": -107
+ },
+ {
+ "x": -302,
+ "y": 38,
+ "z": -105
+ },
+ {
+ "x": -304,
+ "y": 22,
+ "z": -111
+ },
+ {
+ "x": -304,
+ "y": 2,
+ "z": -107
+ },
+ {
+ "x": -304,
+ "y": 40,
+ "z": -107
+ },
+ {
+ "x": -306,
+ "y": 32,
+ "z": -105
+ },
+ {
+ "x": -304,
+ "y": 8,
+ "z": -109
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index d8ef4941..ead46adc 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -5,7 +5,7 @@
"name": "Skyblocker",
"description": "Hypixel Skyblock Mod",
"authors": ["xMrVizzy", "d3dx9", "LifeIsAParadox"],
- "contributors": ["ExternalTime", "Zailer43", "TacoMonkey", "KonaeAkira", "Fix3dll", "null2264", "HyperSoop", "edgarogh", "TheColdPot", "Julienraptor01", "ADON15c", "catandA", "kevinthegreat1", "AzureAaron"],
+ "contributors": ["ExternalTime", "Zailer43", "TacoMonkey", "KonaeAkira", "Fix3dll", "null2264", "HyperSoop", "edgarogh", "TheColdPot", "Julienraptor01", "ADON15c", "catandA", "kevinthegreat1", "AzureAaron", "msg-programs", "lantice3720"],
"contact": {
"homepage": "https://hysky.de",
"sources": "https://github.com/SkyblockerMod/Skyblocker",
@@ -16,20 +16,23 @@
"environment": "client",
"entrypoints": {
"client": [
- "me.xmrvizzy.skyblocker.SkyblockerInitializer"
+ "me.xmrvizzy.skyblocker.SkyblockerMod"
],
"modmenu": [
"me.xmrvizzy.skyblocker.config.modmenu.ModMenuEntry"
+ ],
+ "rei_client": [
+ "me.xmrvizzy.skyblocker.skyblock.rei.SkyblockerREIClientPlugin"
]
},
"mixins": [
"skyblocker.mixins.json"
],
"depends": {
- "fabricloader": ">=0.14.17",
- "fabric-api": ">=0.76.0+1.19.4",
+ "fabricloader": ">=0.14.21",
+ "fabric-api": ">=0.83.0+1.20",
"cloth-config2": "*",
- "minecraft": "~1.19.4"
+ "minecraft": "~1.20"
},
"custom": {
"modmenu": {
diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json
index 4a6be779..701d54e5 100644
--- a/src/main/resources/skyblocker.mixins.json
+++ b/src/main/resources/skyblocker.mixins.json
@@ -3,21 +3,23 @@
"package": "me.xmrvizzy.skyblocker.mixin",
"compatibilityLevel": "JAVA_17",
"client": [
- "ChatHudListenerMixin",
+ "AccessorWorldRenderer",
"ClientPlayerEntityMixin",
- "InGameHudMixin",
- "ItemRendererMixin",
- "LeverBlockMixin",
+ "ClientPlayNetworkHandlerMixin",
+ "DrawContextMixin",
"FarmlandBlockMixin",
- "MinecraftClientMixin",
- "AccessorWorldRenderer",
- "GenericContainerScreenMixin",
"GenericContainerScreenHandlerMixin",
+ "HandledScreenAccessor",
"HandledScreenMixin",
+ "InGameHudMixin",
"InventoryScreenMixin",
+ "LeverBlockMixin",
+ "MinecraftClientMixin",
+ "PlayerListHudAccessor",
+ "PlayerListHudMixin",
"RecipeBookWidgetAccessor",
- "HandledScreenAccessor",
- "ScreenMixin"
+ "accessor.BeaconBlockEntityRendererInvoker",
+ "accessor.FrustumInvoker"
],
"injectors": {
"defaultRequire": 1
diff --git a/src/test/java/me/xmrvizzy/skyblocker/chat/filters/AdFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/chat/filters/AdFilterTest.java
index 73fb5dbd..a769dca2 100644
--- a/src/test/java/me/xmrvizzy/skyblocker/chat/filters/AdFilterTest.java
+++ b/src/test/java/me/xmrvizzy/skyblocker/chat/filters/AdFilterTest.java
@@ -14,47 +14,47 @@ class AdFilterTest extends ChatPatternListenerTest<AdFilter> {
@Test
void noRank() {
- assertMatches("§7Advertiser§7: advertisement");
+ assertMatches("§8[§a86§8] §7Advertiser§7: advertisement");
}
@Test
void vip() {
- assertMatches("§a[VIP] Advertiser§f: advertisement");
+ assertMatches("§8[§b280§8] §a[VIP] Advertiser§f: advertisement");
}
@Test
void mvp() {
- assertMatches("§b[MVP§c+§b] Advertiser§f: advertisement");
+ assertMatches("§8[§d256§8] §b[MVP§c+§b] Advertiser§f: advertisement");
}
@Test
void plusPlus() {
- assertMatches("§6[MVP§c++§6] Advertiser§f: advertisement");
+ assertMatches("§8[§6222§8] §6[MVP§c++§6] Advertiser§f: advertisement");
}
@Test
void capturesMessage() {
- assertGroup("§b[MVP§c+§b] b2dderr§f: buying prismapump", 2, "buying prismapump");
+ assertGroup("§8[§c325§8] §b[MVP§c+§b] b2dderr§f: buying prismapump", 2, "buying prismapump");
}
@Test
void simpleAd() {
- assertFilters("§b[MVP§c+§b] b2dderr§f: buying prismapump");
+ assertFilters("§8[§c320§8] §b[MVP§c+§b] b2dderr§f: buying prismapump");
}
@Test
void uppercaseAd() {
- assertFilters("§a[VIP] Tecnoisnoob§f: SELLING REJUVENATE 5 Book on ah!");
+ assertFilters("§8[§f70§8] §a[VIP] Tecnoisnoob§f: SELLING REJUVENATE 5 Book on ah!");
}
@Test
void characterSpam() {
- assertFilters("§a[VIP] Benyyy_§f: Hey, Visit my Island, i spent lots of time to build it! I also made donate room! <<<<<<<<<<<<<<<<<<<");
+ assertFilters("§8[§9144§8] §a[VIP] Benyyy_§f: Hey, Visit my Island, i spent lots of time to build it! I also made donate room! <<<<<<<<<<<<<<<<<<<");
}
@Test
void notAd() {
- Matcher matcher = listener.pattern.matcher("§a[VIP] NotMatching§f: This message shouldn't match!");
+ Matcher matcher = listener.pattern.matcher("§8[§6200§8] §a[VIP] NotMatching§f: This message shouldn't match!");
assertTrue(matcher.matches());
assertFalse(listener.onMatch(null, matcher));
}
diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java
new file mode 100644
index 00000000..2465f7a2
--- /dev/null
+++ b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java
@@ -0,0 +1,35 @@
+package me.xmrvizzy.skyblocker.skyblock.dungeon;
+
+import me.xmrvizzy.skyblocker.chat.ChatPatternListenerTest;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.regex.Matcher;
+
+public class AcceptRepartyTest extends ChatPatternListenerTest<Reparty> {
+
+ public AcceptRepartyTest() { super(new Reparty()); }
+
+ protected void assertGroup(String message, String group, String expect) {
+ Matcher matcher = matcher(message);
+ assertTrue(matcher.matches());
+ assertEquals(expect, matcher.group(group));
+ }
+
+ @Test
+ void testDisband() {
+ assertGroup("[VIP+] KoloiYolo has disbanded the party!",
+ /* group: */ "disband",
+ /* expect: */ "KoloiYolo");
+ }
+
+ @Test
+ void testInvite() {
+ assertGroup("-----------------------------------------------------" +
+ "\n[MVP+] 1wolvesgaming has invited you to join their party!" +
+ "\nYou have 60 seconds to accept. Click here to join!" +
+ "\n-----------------------------------------------------",
+ /* group: */ "invite",
+ /* expect: */ "1wolvesgaming");
+ }
+} \ No newline at end of file