diff options
59 files changed, 3709 insertions, 804 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 3336cefb..0dc1b409 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -3,10 +3,12 @@ package de.hysky.skyblocker; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import de.hysky.skyblocker.config.datafixer.ConfigDataFixer; import de.hysky.skyblocker.config.ImageRepoLoader; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.debug.Debug; import de.hysky.skyblocker.skyblock.*; +import de.hysky.skyblocker.skyblock.calculators.CalculatorCommand; import de.hysky.skyblocker.skyblock.chat.ChatRuleAnnouncementScreen; import de.hysky.skyblocker.skyblock.chat.ChatRulesHandler; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; @@ -17,10 +19,7 @@ import de.hysky.skyblocker.skyblock.dungeon.puzzle.boulder.Boulder; import de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard.Waterboard; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.dungeon.secrets.SecretsTracker; -import de.hysky.skyblocker.skyblock.dwarven.CrystalsHud; -import de.hysky.skyblocker.skyblock.dwarven.CrystalsLocationsManager; -import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud; -import de.hysky.skyblocker.skyblock.dwarven.MetalDetector; +import de.hysky.skyblocker.skyblock.dwarven.*; import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; import de.hysky.skyblocker.skyblock.end.EnderNodes; import de.hysky.skyblocker.skyblock.end.TheEnd; @@ -77,6 +76,7 @@ public class SkyblockerMod implements ClientModInitializer { public static final String VERSION = SKYBLOCKER_MOD.getMetadata().getVersion().getFriendlyString(); public static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir().resolve(NAMESPACE); public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + public static final Gson GSON_COMPACT = new GsonBuilder().create(); private static SkyblockerMod INSTANCE; public final ContainerSolverManager containerSolverManager = new ContainerSolverManager(); public final StatusBarTracker statusBarTracker = new StatusBarTracker(); @@ -101,6 +101,7 @@ public class SkyblockerMod implements ClientModInitializer { @Override public void onInitializeClient() { ClientTickEvents.END_CLIENT_TICK.register(this::tick); + ConfigDataFixer.apply(); Utils.init(); SkyblockerConfigManager.init(); SkyblockerScreen.initClass(); @@ -119,10 +120,10 @@ public class SkyblockerMod implements ClientModInitializer { EnderNodes.init(); OrderedWaypoints.init(); BackpackPreview.init(); - QuickNav.init(); ItemCooldowns.init(); TabHud.init(); DwarvenHud.init(); + CommissionLabels.init(); CrystalsHud.init(); FarmingHud.init(); LowerSensitivity.init(); @@ -132,6 +133,7 @@ public class SkyblockerMod implements ClientModInitializer { Shortcuts.init(); ChatRulesHandler.init(); ChatRuleAnnouncementScreen.init(); + CalculatorCommand.init(); DiscordRPCManager.init(); LividColor.init(); FishingHelper.init(); diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java index c591ba14..9c495382 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -5,7 +5,7 @@ import dev.isxander.yacl3.config.v2.api.SerialEntry; public class SkyblockerConfig { @SerialEntry - public int version = 2; + public int version = SkyblockerConfigManager.CONFIG_VERSION; @SerialEntry public GeneralConfig general = new GeneralConfig(); @@ -20,6 +20,9 @@ public class SkyblockerConfig { public DungeonsConfig dungeons = new DungeonsConfig(); @SerialEntry + public ForagingConfig foraging = new ForagingConfig(); + + @SerialEntry public CrimsonIsleConfig crimsonIsle = new CrimsonIsleConfig(); @SerialEntry diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java index 4b8e56df..dd406b8a 100644 --- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java @@ -26,10 +26,11 @@ import java.lang.StackWalker.Option; import java.nio.file.Path; public class SkyblockerConfigManager { - private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve("skyblocker-2.json"); + public static final int CONFIG_VERSION = 3; + private static final Path CONFIG_FILE = FabricLoader.getInstance().getConfigDir().resolve("skyblocker.json"); private static final ConfigClassHandler<SkyblockerConfig> HANDLER = ConfigClassHandler.createBuilder(SkyblockerConfig.class) .serializer(config -> GsonConfigSerializerBuilder.create(config) - .setPath(PATH) + .setPath(CONFIG_FILE) .setJson5(false) .appendGsonBuilder(builder -> builder .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) @@ -74,6 +75,7 @@ public class SkyblockerConfigManager { .category(UIAndVisualsCategory.create(defaults, config)) .category(HelperCategory.create(defaults, config)) .category(DungeonsCategory.create(defaults, config)) + //.category(ForagingCategory.create(defaults, config)) .category(CrimsonIsleCategory.create(defaults, config)) .category(MiningCategory.create(defaults, config)) .category(FarmingCategory.create(defaults, config)) diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java index d44ab490..28ace441 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -67,6 +67,14 @@ public class DungeonsCategory { newValue -> config.dungeons.allowDroppingProtectedItems = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.hideSoulweaverSkulls")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.dungeons.hideSoulweaverSkulls.@Tooltip"))) + .binding(defaults.dungeons.hideSoulweaverSkulls, + () -> config.dungeons.hideSoulweaverSkulls, + newValue -> config.dungeons.hideSoulweaverSkulls = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) // Map .group(OptionGroup.createBuilder() @@ -252,6 +260,13 @@ public class DungeonsCategory { newValue -> config.dungeons.terminals.solveStartsWith = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.dungeons.terminals.blockIncorrectClicks")) + .binding(defaults.dungeons.terminals.blockIncorrectClicks, + () -> config.dungeons.terminals.blockIncorrectClicks, + newValue -> config.dungeons.terminals.blockIncorrectClicks = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .build()) // Dungeon Secret Waypoints diff --git a/src/main/java/de/hysky/skyblocker/config/categories/ForagingCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/ForagingCategory.java new file mode 100644 index 00000000..d5c844b7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/ForagingCategory.java @@ -0,0 +1,23 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import dev.isxander.yacl3.api.ConfigCategory; +import net.minecraft.text.Text; + +public class ForagingCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("skyblocker.config.foraging")) + + //Modern Foraging island + + //Hunting - YACL doesn't like empty option groups + /*.group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.foraging.hunting")) + + .build())*/ + + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java index 8809ba44..8dc587fd 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/MiningCategory.java @@ -160,6 +160,56 @@ public class MiningCategory { .build()) .build()) + + //commission waypoints + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.mining.commissionWaypoints")) + .collapsed(false) + .option(Option.<MiningConfig.CommissionWaypointMode>createBuilder() + .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.mode")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[0]"), + Text.translatable("skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[1]"), + Text.translatable("skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[2]"), + Text.translatable("skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[3]"), + Text.translatable("skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[4]"))) + .binding(defaults.mining.commissionWaypoints.mode, + () -> config.mining.commissionWaypoints.mode, + newValue -> config.mining.commissionWaypoints.mode = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.useColor")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.useColor.@Tooltip"))) + .binding(defaults.mining.commissionWaypoints.useColor, + () -> config.mining.commissionWaypoints.useColor, + newValue -> config.mining.commissionWaypoints.useColor = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Float>createBuilder() + .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.textScale")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.textScale.@Tooltip"))) + .binding(defaults.mining.commissionWaypoints.textScale, + () -> config.mining.commissionWaypoints.textScale, + newValue -> config.mining.commissionWaypoints.textScale = newValue) + .controller(FloatFieldControllerBuilder::create) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.showBaseCamp")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.showBaseCamp.@Tooltip"))) + .binding(defaults.mining.commissionWaypoints.showBaseCamp, + () -> config.mining.commissionWaypoints.showBaseCamp, + newValue -> config.mining.commissionWaypoints.showBaseCamp = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.mining.commissionWaypoints.showEmissary")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.mining.commissionWaypoints.showEmissary.@Tooltip"))) + .binding(defaults.mining.commissionWaypoints.showEmissary, + () -> config.mining.commissionWaypoints.showEmissary, + newValue -> config.mining.commissionWaypoints.showEmissary = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java index 1200261d..98e5511c 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java @@ -2,16 +2,18 @@ package de.hysky.skyblocker.config.categories; import de.hysky.skyblocker.config.ConfigUtils; import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.configs.QuickNavigationConfig; import dev.isxander.yacl3.api.ConfigCategory; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.OptionDescription; import dev.isxander.yacl3.api.OptionGroup; import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.ItemControllerBuilder; import dev.isxander.yacl3.api.controller.StringControllerBuilder; +import net.minecraft.item.Item; import net.minecraft.text.Text; public class QuickNavigationCategory { - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { return ConfigCategory.createBuilder() .name(Text.translatable("skyblocker.config.quickNav")) @@ -25,594 +27,71 @@ public class QuickNavigationCategory { .controller(ConfigUtils::createBooleanController) .build()) - //Button 1 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 1)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button1.render, - () -> config.quickNav.button1.render, - newValue -> config.quickNav.button1.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button1.item.id, - () -> config.quickNav.button1.item.id, - newValue -> config.quickNav.button1.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button1.item.count, - () -> config.quickNav.button1.item.count, - newValue -> config.quickNav.button1.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button1.item.nbt, - () -> config.quickNav.button1.item.nbt, - newValue -> config.quickNav.button1.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button1.uiTitle, - () -> config.quickNav.button1.uiTitle, - newValue -> config.quickNav.button1.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button1.clickEvent, - () -> config.quickNav.button1.clickEvent, - newValue -> config.quickNav.button1.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 2 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 2)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button2.render, - () -> config.quickNav.button2.render, - newValue -> config.quickNav.button2.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button2.item.id, - () -> config.quickNav.button2.item.id, - newValue -> config.quickNav.button2.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button2.item.count, - () -> config.quickNav.button2.item.count, - newValue -> config.quickNav.button2.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button2.item.nbt, - () -> config.quickNav.button2.item.nbt, - newValue -> config.quickNav.button2.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button2.uiTitle, - () -> config.quickNav.button2.uiTitle, - newValue -> config.quickNav.button2.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button2.clickEvent, - () -> config.quickNav.button2.clickEvent, - newValue -> config.quickNav.button2.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 3 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 3)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button3.render, - () -> config.quickNav.button3.render, - newValue -> config.quickNav.button3.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button3.item.id, - () -> config.quickNav.button3.item.id, - newValue -> config.quickNav.button3.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button3.item.count, - () -> config.quickNav.button3.item.count, - newValue -> config.quickNav.button3.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button3.item.nbt, - () -> config.quickNav.button3.item.nbt, - newValue -> config.quickNav.button3.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button3.uiTitle, - () -> config.quickNav.button3.uiTitle, - newValue -> config.quickNav.button3.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button3.clickEvent, - () -> config.quickNav.button3.clickEvent, - newValue -> config.quickNav.button3.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 4 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 4)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button4.render, - () -> config.quickNav.button4.render, - newValue -> config.quickNav.button4.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button4.item.id, - () -> config.quickNav.button4.item.id, - newValue -> config.quickNav.button4.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button4.item.count, - () -> config.quickNav.button4.item.count, - newValue -> config.quickNav.button4.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button4.item.nbt, - () -> config.quickNav.button4.item.nbt, - newValue -> config.quickNav.button4.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button4.uiTitle, - () -> config.quickNav.button4.uiTitle, - newValue -> config.quickNav.button4.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button4.clickEvent, - () -> config.quickNav.button4.clickEvent, - newValue -> config.quickNav.button4.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 5 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 5)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button5.render, - () -> config.quickNav.button5.render, - newValue -> config.quickNav.button5.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button5.item.id, - () -> config.quickNav.button5.item.id, - newValue -> config.quickNav.button5.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button5.item.count, - () -> config.quickNav.button5.item.count, - newValue -> config.quickNav.button5.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button5.item.nbt, - () -> config.quickNav.button5.item.nbt, - newValue -> config.quickNav.button5.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button5.uiTitle, - () -> config.quickNav.button5.uiTitle, - newValue -> config.quickNav.button5.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button5.clickEvent, - () -> config.quickNav.button5.clickEvent, - newValue -> config.quickNav.button5.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 6 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 6)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button6.render, - () -> config.quickNav.button6.render, - newValue -> config.quickNav.button6.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button6.item.id, - () -> config.quickNav.button6.item.id, - newValue -> config.quickNav.button6.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button6.item.count, - () -> config.quickNav.button6.item.count, - newValue -> config.quickNav.button6.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button6.item.nbt, - () -> config.quickNav.button6.item.nbt, - newValue -> config.quickNav.button6.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button6.uiTitle, - () -> config.quickNav.button6.uiTitle, - newValue -> config.quickNav.button6.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button6.clickEvent, - () -> config.quickNav.button6.clickEvent, - newValue -> config.quickNav.button6.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) + //Buttons + .group(quickNavButton(defaults.quickNav.button1, config.quickNav.button1, 1)) + .group(quickNavButton(defaults.quickNav.button2, config.quickNav.button2, 2)) + .group(quickNavButton(defaults.quickNav.button3, config.quickNav.button3, 3)) + .group(quickNavButton(defaults.quickNav.button4, config.quickNav.button4, 4)) + .group(quickNavButton(defaults.quickNav.button5, config.quickNav.button5, 5)) + .group(quickNavButton(defaults.quickNav.button6, config.quickNav.button6, 6)) + .group(quickNavButton(defaults.quickNav.button7, config.quickNav.button7, 7)) + .group(quickNavButton(defaults.quickNav.button8, config.quickNav.button8, 8)) + .group(quickNavButton(defaults.quickNav.button9, config.quickNav.button9, 9)) + .group(quickNavButton(defaults.quickNav.button10, config.quickNav.button10, 10)) + .group(quickNavButton(defaults.quickNav.button11, config.quickNav.button11, 11)) + .group(quickNavButton(defaults.quickNav.button12, config.quickNav.button12, 12)) + .group(quickNavButton(defaults.quickNav.button13, config.quickNav.button13, 13)) + .group(quickNavButton(defaults.quickNav.button14, config.quickNav.button14, 14)) + .build(); + } - //Button 7 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 7)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button7.render, - () -> config.quickNav.button7.render, - newValue -> config.quickNav.button7.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button7.item.id, - () -> config.quickNav.button7.item.id, - newValue -> config.quickNav.button7.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button7.item.count, - () -> config.quickNav.button7.item.count, - newValue -> config.quickNav.button7.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button7.item.nbt, - () -> config.quickNav.button7.item.nbt, - newValue -> config.quickNav.button7.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button7.uiTitle, - () -> config.quickNav.button7.uiTitle, - newValue -> config.quickNav.button7.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button7.clickEvent, - () -> config.quickNav.button7.clickEvent, - newValue -> config.quickNav.button7.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) + private static OptionGroup quickNavButton(QuickNavigationConfig.QuickNavItem defaultButton, QuickNavigationConfig.QuickNavItem button, int index) { + return OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button", index)) + .collapsed(true) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button.render")) + .binding(defaultButton.render, + () -> button.render, + newValue -> button.render = newValue) + .controller(ConfigUtils::createBooleanController) .build()) - - //Button 8 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 8)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button8.render, - () -> config.quickNav.button8.render, - newValue -> config.quickNav.button8.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button8.item.id, - () -> config.quickNav.button8.item.id, - newValue -> config.quickNav.button8.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button8.item.count, - () -> config.quickNav.button8.item.count, - newValue -> config.quickNav.button8.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button8.item.nbt, - () -> config.quickNav.button8.item.nbt, - newValue -> config.quickNav.button8.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button8.uiTitle, - () -> config.quickNav.button8.uiTitle, - newValue -> config.quickNav.button8.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button8.clickEvent, - () -> config.quickNav.button8.clickEvent, - newValue -> config.quickNav.button8.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) + .option(Option.<Item>createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) + .binding(defaultButton.itemData.item, + () -> button.itemData.item, + newValue -> button.itemData.item = newValue) + .controller(ItemControllerBuilder::create) .build()) - - //Button 9 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 9)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button9.render, - () -> config.quickNav.button9.render, - newValue -> config.quickNav.button9.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button9.item.id, - () -> config.quickNav.button9.item.id, - newValue -> config.quickNav.button9.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button9.item.count, - () -> config.quickNav.button9.item.count, - newValue -> config.quickNav.button9.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button9.item.nbt, - () -> config.quickNav.button9.item.nbt, - newValue -> config.quickNav.button9.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button9.uiTitle, - () -> config.quickNav.button9.uiTitle, - newValue -> config.quickNav.button9.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button9.clickEvent, - () -> config.quickNav.button9.clickEvent, - newValue -> config.quickNav.button9.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) + .option(Option.<Integer>createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) + .binding(defaultButton.itemData.count, + () -> button.itemData.count, + newValue -> button.itemData.count = newValue) + .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 99)) .build()) - - //Button 10 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 10)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button10.render, - () -> config.quickNav.button10.render, - newValue -> config.quickNav.button10.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button10.item.id, - () -> config.quickNav.button10.item.id, - newValue -> config.quickNav.button10.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button10.item.count, - () -> config.quickNav.button10.item.count, - newValue -> config.quickNav.button10.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button10.item.nbt, - () -> config.quickNav.button10.item.nbt, - newValue -> config.quickNav.button10.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button10.uiTitle, - () -> config.quickNav.button10.uiTitle, - newValue -> config.quickNav.button10.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button10.clickEvent, - () -> config.quickNav.button10.clickEvent, - newValue -> config.quickNav.button10.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) + .option(Option.<String>createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button.item.components")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.components.@Tooltip"))) + .binding(defaultButton.itemData.components, + () -> button.itemData.components, + newValue -> button.itemData.components = newValue) + .controller(StringControllerBuilder::create) .build()) - - //Button 11 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 11)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button11.render, - () -> config.quickNav.button11.render, - newValue -> config.quickNav.button11.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button11.item.id, - () -> config.quickNav.button11.item.id, - newValue -> config.quickNav.button11.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button11.item.count, - () -> config.quickNav.button11.item.count, - newValue -> config.quickNav.button11.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button11.item.nbt, - () -> config.quickNav.button11.item.nbt, - newValue -> config.quickNav.button11.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button11.uiTitle, - () -> config.quickNav.button11.uiTitle, - newValue -> config.quickNav.button11.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button11.clickEvent, - () -> config.quickNav.button11.clickEvent, - newValue -> config.quickNav.button11.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) + .option(Option.<String>createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) + .binding(defaultButton.uiTitle, + () -> button.uiTitle, + newValue -> button.uiTitle = newValue) + .controller(StringControllerBuilder::create) .build()) - - //Button 12 - .group(OptionGroup.createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button", 12)) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.render")) - .binding(defaults.quickNav.button12.render, - () -> config.quickNav.button12.render, - newValue -> config.quickNav.button12.render = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.itemName")) - .binding(defaults.quickNav.button12.item.id, - () -> config.quickNav.button12.item.id, - newValue -> config.quickNav.button12.item.id = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.count")) - .binding(defaults.quickNav.button12.item.count, - () -> config.quickNav.button12.item.count, - newValue -> config.quickNav.button12.item.count = newValue) - .controller(opt -> IntegerFieldControllerBuilder.create(opt).range(1, 64)) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.item.nbt")) - .description(OptionDescription.of(Text.translatable("skyblocker.config.quickNav.button.item.nbt.@Tooltip"))) - .binding(defaults.quickNav.button12.item.nbt, - () -> config.quickNav.button12.item.nbt, - newValue -> config.quickNav.button12.item.nbt = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.uiTitle")) - .binding(defaults.quickNav.button12.uiTitle, - () -> config.quickNav.button12.uiTitle, - newValue -> config.quickNav.button12.uiTitle = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) - .binding(defaults.quickNav.button12.clickEvent, - () -> config.quickNav.button12.clickEvent, - newValue -> config.quickNav.button12.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) + .option(Option.<String>createBuilder() + .name(Text.translatable("skyblocker.config.quickNav.button.clickEvent")) + .binding(defaultButton.clickEvent, + () -> button.clickEvent, + newValue -> button.clickEvent = newValue) + .controller(StringControllerBuilder::create) .build()) - .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java index c6936335..369a9a90 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java @@ -8,12 +8,15 @@ import de.hysky.skyblocker.config.configs.UIAndVisualsConfig; import de.hysky.skyblocker.utils.waypoint.Waypoint; import dev.isxander.yacl3.api.ConfigCategory; import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.api.controller.ColorControllerBuilder; import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import java.awt.*; + public class UIAndVisualsCategory { public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { return ConfigCategory.createBuilder() @@ -333,6 +336,28 @@ public class UIAndVisualsCategory { .build()) .build()) + //Input Calculator + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator")) + .collapsed(true) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator.enabled")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator.enabled.@Tooltip"))) + .binding(defaults.uiAndVisuals.inputCalculator.enabled, + () -> config.uiAndVisuals.inputCalculator.enabled, + newValue -> config.uiAndVisuals.inputCalculator.enabled = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator.requiresEquals")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator.requiresEquals.@Tooltip"))) + .binding(defaults.uiAndVisuals.inputCalculator.requiresEquals, + () -> config.uiAndVisuals.inputCalculator.requiresEquals, + newValue -> config.uiAndVisuals.inputCalculator.requiresEquals = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + //Flame Overlay .group(OptionGroup.createBuilder() .name(Text.translatable("skyblocker.config.uiAndVisuals.flameOverlay")) @@ -355,6 +380,49 @@ public class UIAndVisualsCategory { .build()) .build()) + //Compact Damage Numbers + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage")) + .collapsed(true) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage.enabled")) + .binding(defaults.uiAndVisuals.compactDamage.enabled, + () -> config.uiAndVisuals.compactDamage.enabled, + newValue -> config.uiAndVisuals.compactDamage.enabled = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Integer>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage.precision")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage.precision.@Tooltip"))) + .binding(defaults.uiAndVisuals.compactDamage.precision, + () -> config.uiAndVisuals.compactDamage.precision, + newValue -> config.uiAndVisuals.compactDamage.precision = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1,3).step(1)) + .build()) + .option(Option.<Color>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage.normalDamageColor")) + .binding(defaults.uiAndVisuals.compactDamage.normalDamageColor, + () -> config.uiAndVisuals.compactDamage.normalDamageColor, + newValue -> config.uiAndVisuals.compactDamage.normalDamageColor = newValue) + .controller(ColorControllerBuilder::create) + .build()) + .option(Option.<Color>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage.critDamageGradientStart")) + .binding(defaults.uiAndVisuals.compactDamage.critDamageGradientStart, + () -> config.uiAndVisuals.compactDamage.critDamageGradientStart, + newValue -> config.uiAndVisuals.compactDamage.critDamageGradientStart = newValue) + .controller(ColorControllerBuilder::create) + .build()) + .option(Option.<Color>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.compactDamage.critDamageGradientEnd")) + .binding(defaults.uiAndVisuals.compactDamage.critDamageGradientEnd, + () -> config.uiAndVisuals.compactDamage.critDamageGradientEnd, + newValue -> config.uiAndVisuals.compactDamage.critDamageGradientEnd = newValue) + .controller(ColorControllerBuilder::create) + .build()) + .build() + ) + .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java index 2c99dc56..7b394b53 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java @@ -24,6 +24,9 @@ public class DungeonsConfig { public boolean allowDroppingProtectedItems = false; @SerialEntry + public boolean hideSoulweaverSkulls = false; + + @SerialEntry public DungeonMap dungeonMap = new DungeonMap(); @SerialEntry @@ -127,6 +130,9 @@ public class DungeonsConfig { @SerialEntry public boolean solveStartsWith = true; + + @SerialEntry + public boolean blockIncorrectClicks = false; } public static class SecretWaypoints { diff --git a/src/main/java/de/hysky/skyblocker/config/configs/ForagingConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/ForagingConfig.java new file mode 100644 index 00000000..1a7c3598 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/configs/ForagingConfig.java @@ -0,0 +1,13 @@ +package de.hysky.skyblocker.config.configs; + +import dev.isxander.yacl3.config.v2.api.SerialEntry; + +public class ForagingConfig { + + @SerialEntry + public Hunting hunting = new Hunting(); + + public static class Hunting { + + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java index fe845e55..65fd63ca 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/MiningConfig.java @@ -21,6 +21,9 @@ public class MiningConfig { @SerialEntry public CrystalsWaypoints crystalsWaypoints = new CrystalsWaypoints(); + @SerialEntry + public CommissionWaypoints commissionWaypoints = new CommissionWaypoints(); + public static class DwarvenMines { @SerialEntry public boolean solveFetchur = true; @@ -85,6 +88,37 @@ public class MiningConfig { public boolean findInChat = true; } + public static class CommissionWaypoints { + @SerialEntry + public CommissionWaypointMode mode = CommissionWaypointMode.BOTH; + + @SerialEntry + public boolean useColor = true; + + @SerialEntry + public float textScale = 1; + + @SerialEntry + public boolean showBaseCamp = false; + + @SerialEntry + public boolean showEmissary = true; + } + + public enum CommissionWaypointMode { + OFF, DWARVEN, GLACITE, BOTH; + + @Override + public String toString() { + return switch (this) { + case OFF -> "Off"; + case DWARVEN -> "Dwarven"; + case GLACITE -> "Glacite"; + case BOTH -> "Both"; + }; + } + } + public enum DwarvenHudStyle { SIMPLE, FANCY, CLASSIC; diff --git a/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java index 2c4347b6..20a0c9cc 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/QuickNavigationConfig.java @@ -1,6 +1,9 @@ package de.hysky.skyblocker.config.configs; import dev.isxander.yacl3.config.v2.api.SerialEntry; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; +import net.minecraft.util.Identifier; public class QuickNavigationConfig { @SerialEntry @@ -24,14 +27,13 @@ public class QuickNavigationConfig { * " \\([12]\\/2\\)" : match on the page either " (1/2)" or " (2/2)" */ @SerialEntry - public QuickNavItem button4 = new QuickNavItem(true, - new ItemData("leather_chestplate", 1, "tag:{display:{color:8991416}}"), "Wardrobe \\([12]/2\\)", - "/wardrobe"); + public QuickNavItem button4 = new QuickNavItem(true, new ItemData("leather_chestplate", 1, "[minecraft:dyed_color={rgb:8991416}]"), "Wardrobe \\([12]/2\\)", "/wardrobe"); @SerialEntry - public QuickNavItem button5 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;-2081424676,-57521078,-2073572414,158072763],Properties:{textures:[{Value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}}}"), - "Sack of Sacks", "/sacks"); + public QuickNavItem button5 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={id:[I;-2081424676,-57521078,-2073572414,158072763],name:\"\",properties:[{name:\"textures\",value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}]"), "Sack of Sacks", "/sacks"); + + @SerialEntry + public QuickNavItem button6 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={name:\"5da6bec64bd942bc\",id:[I;1571208902,1272529596,-1566400349,-679283814],properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTYxYTkxOGMwYzQ5YmE4ZDA1M2U1MjJjYjkxYWJjNzQ2ODkzNjdiNGQ4YWEwNmJmYzFiYTkxNTQ3MzA5ODVmZiJ9fX0=\"}]}]"), "Accessory Bag(?: \\(\\d/\\d\\))?", "/accessories"); /* REGEX Explanation * "(?:Rift )?" : optional match on the non-capturing group "Rift " @@ -39,115 +41,42 @@ public class QuickNavigationConfig { * "(?: \\([12]\\/2\\))?" : optional match on the non-capturing group " (1/2)" or " (2/2)" */ @SerialEntry - public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), - "(?:Rift )?Storage(?: \\(1/2\\))?", "/storage"); + public QuickNavItem button7 = new QuickNavItem(true, new ItemData("ender_chest"), "(?:Rift )?Storage(?: \\(\\d/\\d\\))?", "/storage"); @SerialEntry - public QuickNavItem button7 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}"), - "none", "/hub"); + public QuickNavItem button8 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={name:\"421a8ef40eff47f4\",id:[I;1109036788,251611124,-2126904485,-130621758],properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzljODg4MWU0MjkxNWE5ZDI5YmI2MWExNmZiMjZkMDU5OTEzMjA0ZDI2NWRmNWI0MzliM2Q3OTJhY2Q1NiJ9fX0=\"}]}]"), "", "/is"); @SerialEntry - public QuickNavItem button8 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}"), - "none", "/warp dungeon_hub"); + public QuickNavItem button9 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={name:\"e30e30d02878417c\",id:[I;-485609264,678969724,-1929747597,-718202427],properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjQ4ODBkMmMxZTdiODZlODc1MjJlMjA4ODI2NTZmNDViYWZkNDJmOTQ5MzJiMmM1ZTBkNmVjYWE0OTBjYjRjIn19fQ==\"}]}]"), "", "/warp garden"); @SerialEntry - public QuickNavItem button9 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;-562285948,532499670,-1705302742,775653035],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}}}"), - "Visit prtl", "/visit prtl"); + public QuickNavItem button10 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={id:[I;-300151517,-631415889,-1193921967,-1821784279],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}]"), "none", "/hub"); @SerialEntry - public QuickNavItem button10 = new QuickNavItem(true, new ItemData("enchanting_table"), "Enchant Item", - "/etable"); - + public QuickNavItem button11 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={id:[I;1605800870,415127827,-1236127084,15358548],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}]"), "none", "/warp dungeon_hub"); @SerialEntry - public QuickNavItem button11 = new QuickNavItem(true, new ItemData("anvil"), "Anvil", "/anvil"); + public QuickNavItem button12 = new QuickNavItem(true, new ItemData("gold_block"), "", "/ah"); @SerialEntry - public QuickNavItem button12 = new QuickNavItem(true, new ItemData("crafting_table"), "Craft Item", "/craft"); - - public static class QuickNav { - @SerialEntry - public boolean enableQuickNav = true; - - @SerialEntry - public QuickNavItem button1 = new QuickNavItem(true, new ItemData("diamond_sword"), "Your Skills", "/skills"); - - @SerialEntry - public QuickNavItem button2 = new QuickNavItem(true, new ItemData("painting"), "Collections", "/collection"); - - /* REGEX Explanation - * "Pets" : simple match on letters - * "(?: \\(\\d+\\/\\d+\\))?" : optional match on the non-capturing group for the page in the format " ($number/$number)" - */ - @SerialEntry - public QuickNavItem button3 = new QuickNavItem(true, new ItemData("bone"), "Pets(:? \\(\\d+\\/\\d+\\))?", "/pets"); - - /* REGEX Explanation - * "Wardrobe" : simple match on letters - * " \\([12]\\/2\\)" : match on the page either " (1/2)" or " (2/2)" - */ - @SerialEntry - public QuickNavItem button4 = new QuickNavItem(true, - new ItemData("leather_chestplate", 1, "tag:{display:{color:8991416}}"), "Wardrobe \\([12]/2\\)", - "/wardrobe"); - - @SerialEntry - public QuickNavItem button5 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;-2081424676,-57521078,-2073572414,158072763],Properties:{textures:[{Value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}}}"), - "Sack of Sacks", "/sacks"); - - /* REGEX Explanation - * "(?:Rift )?" : optional match on the non-capturing group "Rift " - * "Storage" : simple match on letters - * "(?: \\([12]\\/2\\))?" : optional match on the non-capturing group " (1/2)" or " (2/2)" - */ - @SerialEntry - public QuickNavItem button6 = new QuickNavItem(true, new ItemData("ender_chest"), - "(?:Rift )?Storage(?: \\(1/2\\))?", "/storage"); - - @SerialEntry - public QuickNavItem button7 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}"), - "none", "/hub"); + public QuickNavItem button13 = new QuickNavItem(true, new ItemData("player_head", 1, "[minecraft:profile={id:[I;-562285948,532499670,-1705302742,775653035],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZmZlMmRjZGE0MWVjM2FmZjhhZjUwZjI3MmVjMmUwNmE4ZjUwOWUwZjgwN2YyMzU1YTFmNWEzM2MxYjY2ZTliNCJ9fX0=\"}]}]"), "Bazaar .*", "/bz"); - @SerialEntry - public QuickNavItem button8 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}"), - "none", "/warp dungeon_hub"); - - @SerialEntry - public QuickNavItem button9 = new QuickNavItem(true, new ItemData("player_head", 1, - "tag:{SkullOwner:{Id:[I;-562285948,532499670,-1705302742,775653035],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}}}"), - "Visit prtl", "/visit prtl"); - - @SerialEntry - public QuickNavItem button10 = new QuickNavItem(true, new ItemData("enchanting_table"), "Enchant Item", - "/etable"); - - - @SerialEntry - public QuickNavItem button11 = new QuickNavItem(true, new ItemData("anvil"), "Anvil", "/anvil"); - - @SerialEntry - public QuickNavItem button12 = new QuickNavItem(true, new ItemData("crafting_table"), "Craft Item", "/craft"); - } + @SerialEntry + public QuickNavItem button14 = new QuickNavItem(true, new ItemData("crafting_table"), "Craft Item", "/craft"); public static class QuickNavItem { public QuickNavItem(Boolean render, ItemData itemData, String uiTitle, String clickEvent) { this.render = render; - this.item = itemData; + this.itemData = itemData; this.clickEvent = clickEvent; this.uiTitle = uiTitle; } @SerialEntry - public Boolean render; + public boolean render; @SerialEntry - public ItemData item; + public ItemData itemData; @SerialEntry public String uiTitle; @@ -157,25 +86,31 @@ public class QuickNavigationConfig { } public static class ItemData { - public ItemData(String id, int count, String nbt) { - this.id = id; - this.count = count; - this.nbt = nbt; + public ItemData(String item) { + this(item, 1, "[]"); } - public ItemData(String id) { - this.id = id; - this.count = 1; - this.nbt = ""; + public ItemData(String item, int count, String components) { + this(Registries.ITEM.get(new Identifier(item)), count, components); + } + + public ItemData(Item item) { + this(item, 1, "[]"); + } + + public ItemData(Item item, int count, String components) { + this.item = item; + this.count = count; + this.components = components; } @SerialEntry - public String id; + public Item item; @SerialEntry public int count; @SerialEntry - public String nbt; + public String components; } } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java index 267dde14..e016988b 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.utils.waypoint.Waypoint; import dev.isxander.yacl3.config.v2.api.SerialEntry; import net.minecraft.util.Formatting; +import java.awt.*; import java.util.ArrayList; import java.util.List; @@ -54,8 +55,14 @@ public class UIAndVisualsConfig { public SearchOverlay searchOverlay = new SearchOverlay(); @SerialEntry + public InputCalculator inputCalculator = new InputCalculator(); + + @SerialEntry public FlameOverlay flameOverlay = new FlameOverlay(); + @SerialEntry + public CompactDamage compactDamage = new CompactDamage(); + public static class ChestValue { @SerialEntry public boolean enableChestValue = true; @@ -239,6 +246,14 @@ public class UIAndVisualsConfig { public List<String> auctionHistory = new ArrayList<>(); } + public static class InputCalculator { + @SerialEntry + public boolean enabled = true; + + @SerialEntry + public boolean requiresEquals = false; + } + public static class FlameOverlay { @SerialEntry public int flameHeight = 100; @@ -247,4 +262,20 @@ public class UIAndVisualsConfig { public int flameOpacity = 100; } + public static class CompactDamage { + @SerialEntry + public boolean enabled = true; + + @SerialEntry + public int precision = 1; + + @SerialEntry + public Color normalDamageColor = new Color(0xFFFFFF); + + @SerialEntry + public Color critDamageGradientStart = new Color(0xFFFF55); + + @SerialEntry + public Color critDamageGradientEnd = new Color(0xFF5555); + } } diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFix.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFix.java new file mode 100644 index 00000000..9ec0e2b8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFix.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.config.datafixer; + +import com.mojang.datafixers.DataFix; +import com.mojang.datafixers.DataFixUtils; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; + +public abstract class ConfigDataFix extends DataFix { + public ConfigDataFix(Schema outputSchema, boolean changesType) { + super(outputSchema, changesType); + } + + protected <T> Dynamic<T> fixVersion(Dynamic<T> dynamic) { + return dynamic.set("version", dynamic.createInt(DataFixUtils.getVersion(getVersionKey()))); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java new file mode 100644 index 00000000..f4e4aad1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixer.java @@ -0,0 +1,96 @@ +package de.hysky.skyblocker.config.datafixer; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.DataFixer; +import com.mojang.datafixers.DataFixerBuilder; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.JsonOps; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.datafixer.JsonHelper; +import net.fabricmc.loader.api.FabricLoader; +import org.slf4j.Logger; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.nio.file.Files; +import java.nio.file.Path; + +public class ConfigDataFixer { + protected static final Logger LOGGER = LogUtils.getLogger(); + private static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir(); + public static final DSL.TypeReference CONFIG_TYPE = () -> "config"; + + public static void apply() { + apply(CONFIG_DIR.resolve("skyblocker.json"), CONFIG_DIR.resolve("skyblocker.json.old")); + } + + public static void apply(Path configDir, Path backupDir) { + //User is new - has no config file (or maybe config folder) + if (!Files.exists(CONFIG_DIR) || !Files.exists(configDir)) return; + + //Should never be null if the file exists unless its malformed JSON or something in which case well it gets reset + JsonObject oldConfig = loadConfig(configDir); + if (oldConfig == null || JsonHelper.getInt(oldConfig, "version").orElse(1) == SkyblockerConfigManager.CONFIG_VERSION) return; + + JsonObject newConfig = apply(oldConfig); + + //Write the updated file + if (!writeConfig(configDir, newConfig)) { + LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config Data Fixer] Failed to fix up config file!"); + writeConfig(backupDir, oldConfig); + } + } + + public static JsonObject apply(JsonObject oldConfig) { + return apply(oldConfig, SkyblockerConfigManager.CONFIG_VERSION); + } + + public static JsonObject apply(JsonObject oldConfig, int newVersion) { + long start = System.currentTimeMillis(); + + JsonObject newConfig = build().update(CONFIG_TYPE, new Dynamic<>(JsonOps.INSTANCE, oldConfig), JsonHelper.getInt(oldConfig, "version").orElse(1), newVersion).getValue().getAsJsonObject(); + + long end = System.currentTimeMillis(); + LOGGER.info("[Skyblocker Config Data Fixer] Applied datafixers in {} ms!", end - start); + return newConfig; + } + + private static DataFixer build() { + DataFixerBuilder builder = new DataFixerBuilder(SkyblockerConfigManager.CONFIG_VERSION); + + builder.addSchema(1, ConfigSchema::new); + Schema schema2 = builder.addSchema(2, Schema::new); + builder.addFixer(new ConfigFix1(schema2, true)); + Schema schema3 = builder.addSchema(3, Schema::new); + builder.addFixer(new ConfigFix2QuickNav(schema3, true)); + + return builder.buildUnoptimized(); + } + + private static JsonObject loadConfig(Path path) { + try (BufferedReader reader = Files.newBufferedReader(path)) { + return JsonParser.parseReader(reader).getAsJsonObject(); + } catch (Throwable t) { + LOGGER.error("[Skyblocker Config Data Fixer] Failed to load config file!", t); + } + + return null; + } + + private static boolean writeConfig(Path path, JsonObject config) { + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + SkyblockerMod.GSON.toJson(config, writer); + + return true; + } catch (Throwable t) { + LOGGER.error("[Skyblocker Config Data Fixer] Failed to save config file at {}!", path, t); + } + + return false; + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java new file mode 100644 index 00000000..8eff09e7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix1.java @@ -0,0 +1,211 @@ +package de.hysky.skyblocker.config.datafixer; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.OptionalDynamic; +import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.StringNbtReader; + +import java.util.Locale; + +public class ConfigFix1 extends ConfigDataFix { + public ConfigFix1(Schema outputSchema, boolean changesType) { + super(outputSchema, changesType); + } + + @Override + protected TypeRewriteRule makeRule() { + return fixTypeEverywhereTyped( + "ConfigFix1", + getInputSchema().getType(ConfigDataFixer.CONFIG_TYPE), + typed -> typed.update(DSL.remainderFinder(), this::fix) + ); + } + + private <T> Dynamic<T> fix(Dynamic<T> dynamic) { + return fixMisc(fixQuickNav(fixChat(fixSlayers(fixOtherLocations(fixFarming(fixMining(fixCrimsonIsle(fixDungeons(fixHelpers(fixUIAndVisuals(fixGeneral(fixVersion(dynamic))))))))))))); + } + + private static <T> Dynamic<T> fixGeneral(Dynamic<T> dynamic) { + return dynamic.update("general", general -> general.update("itemTooltip", itemTooltip -> itemTooltip.setFieldIfPresent("dungeonQuality", general.get("dungeonQuality").result())).remove("dungeonQuality")); + } + + private static <T> Dynamic<T> fixUIAndVisuals(Dynamic<T> dynamic) { + OptionalDynamic<T> general = dynamic.get("general"); + return dynamic.set("uiAndVisuals", dynamic.emptyMap() + .setFieldIfPresent("compactorDeletorPreview", general.get("compactorDeletorPreview").result()) + .setFieldIfPresent("dontStripSkinAlphaValues", general.get("dontStripSkinAlphaValues").result()) + .setFieldIfPresent("backpackPreviewWithoutShift", general.get("backpackPreviewWithoutShift").result()) + .setFieldIfPresent("hideEmptyTooltips", general.get("hideEmptyTooltips").result()) + .setFieldIfPresent("fancyCraftingTable", general.get("fancyCraftingTable").result()) + .setFieldIfPresent("hideStatusEffectOverlay", general.get("hideStatusEffectOverlay").result()) + .setFieldIfPresent("chestValue", general.get("chestValue").result()) + .setFieldIfPresent("itemCooldown", general.get("itemCooldown").result()) + .setFieldIfPresent("titleContainer", general.get("titleContainer").result()) + .setFieldIfPresent("tabHud", general.get("tabHud").result()) + .setFieldIfPresent("fancyAuctionHouse", general.get("fancyAuctionHouse").result()) + .setFieldIfPresent("bars", general.get("bars").result()) + .setFieldIfPresent("waypoints", general.get("waypoints").result()) + .setFieldIfPresent("teleportOverlay", general.get("teleportOverlay").result()) + .setFieldIfPresent("searchOverlay", general.get("searchOverlay").result()) + .setFieldIfPresent("flameOverlay", general.get("flameOverlay").result()) + ).update("general", newGeneral -> newGeneral + .remove("compactorDeletorPreview") + .remove("dontStripSkinAlphaValues") + .remove("backpackPreviewWithoutShift") + .remove("hideEmptyTooltips") + .remove("fancyCraftingTable") + .remove("hideStatusEffectOverlay") + .remove("chestValue") + .remove("itemCooldown") + .remove("titleContainer") + .remove("tabHud") + .remove("fancyAuctionHouse") + .remove("bars") + .remove("waypoints") + .remove("teleportOverlay") + .remove("searchOverlay") + .remove("flameOverlay") + ); + } + + private static <T> Dynamic<T> fixHelpers(Dynamic<T> dynamic) { + OptionalDynamic<T> general = dynamic.get("general"); + return dynamic.set("helpers", dynamic.emptyMap() + .setFieldIfPresent("enableNewYearCakesHelper", general.get("enableNewYearCakesHelper").result()) + .setFieldIfPresent("mythologicalRitual", general.get("mythologicalRitual").result()) + .setFieldIfPresent("experiments", general.get("experiments").result()) + .setFieldIfPresent("fishing", general.get("fishing").result()) + .setFieldIfPresent("fairySouls", general.get("fairySouls").result()) + ).update("general", newGeneral -> newGeneral + .remove("enableNewYearCakesHelper") + .remove("mythologicalRitual") + .remove("experiments") + .remove("fishing") + .remove("fairySouls") + ); + } + + private static <T> Dynamic<T> fixDungeons(Dynamic<T> dynamic) { + OptionalDynamic<T> general = dynamic.get("general"); + OptionalDynamic<T> dungeons = dynamic.get("locations").get("dungeons"); + return dynamic.set("dungeons", dynamic.emptyMap() + .setFieldIfPresent("fancyPartyFinder", general.get("betterPartyFinder").result()) + .setFieldIfPresent("croesusHelper", dungeons.get("croesusHelper").result()) + .setFieldIfPresent("playerSecretsTracker", dungeons.get("playerSecretsTracker").result()) + .setFieldIfPresent("starredMobGlow", dungeons.get("starredMobGlow").result()) + .setFieldIfPresent("starredMobBoundingBoxes", dungeons.get("starredMobBoundingBoxes").result()) + .setFieldIfPresent("allowDroppingProtectedItems", dungeons.get("allowDroppingProtectedItems").result()) + .set("dungeonMap", dynamic.emptyMap() + .setFieldIfPresent("enableMap", dungeons.get("enableMap").result()) + .setFieldIfPresent("mapScaling", dungeons.get("mapScaling").result()) + .setFieldIfPresent("mapX", dungeons.get("mapX").result()) + .setFieldIfPresent("mapY", dungeons.get("mapY").result()) + ) + .set("puzzleSolvers", dynamic.emptyMap() + .setFieldIfPresent("solveThreeWeirdos", dungeons.get("solveThreeWeirdos").result()) + .setFieldIfPresent("blazeSolver", dungeons.get("blazeSolver").result()) + .setFieldIfPresent("creeperSolver", dungeons.get("creeperSolver").result()) + .setFieldIfPresent("solveTrivia", dungeons.get("solveTrivia").result()) + .setFieldIfPresent("solveTicTacToe", dungeons.get("solveTicTacToe").result()) + .setFieldIfPresent("solveWaterboard", dungeons.get("solveWaterboard").result()) + .setFieldIfPresent("solveBoulder", dungeons.get("solveBoulder").result()) + .setFieldIfPresent("solveIceFill", dungeons.get("solveIceFill").result()) + .setFieldIfPresent("solveSilverfish", dungeons.get("solveSilverfish").result()) + ) + .set("theProfessor", dynamic.emptyMap() + .setFieldIfPresent("fireFreezeStaffTimer", dungeons.get("fireFreezeStaffTimer").result()) + .setFieldIfPresent("floor3GuardianHealthDisplay", dungeons.get("floor3GuardianHealthDisplay").result()) + ) + .setFieldIfPresent("livid", dungeons.get("lividColor").result()) + .setFieldIfPresent("terminals", dungeons.get("terminals").result()) + .setFieldIfPresent("secretWaypoints", dungeons.get("secretWaypoints").result()) + .setFieldIfPresent("mimicMessage", dungeons.get("mimicMessage").result()) + .setFieldIfPresent("doorHighlight", dungeons.get("doorHighlight").result()) + .setFieldIfPresent("dungeonScore", dungeons.get("dungeonScore").result()) + .setFieldIfPresent("dungeonChestProfit", dungeons.get("dungeonChestProfit").result()) + ).update("locations", locations -> locations.remove("dungeons")).update("general", newGeneral -> newGeneral.remove("betterPartyFinder")); + } + + private static <T> Dynamic<T> fixCrimsonIsle(Dynamic<T> dynamic) { + return dynamic.setFieldIfPresent("crimsonIsle", dynamic.get("locations").get("crimsonIsle").result()).update("locations", locations -> locations.remove("crimsonIsle")); + } + + private static <T> Dynamic<T> fixMining(Dynamic<T> dynamic) { + OptionalDynamic<T> dwarvenMines = dynamic.get("locations").get("dwarvenMines"); + return dynamic.set("mining", dynamic.emptyMap() + .setFieldIfPresent("enableDrillFuel", dwarvenMines.get("enableDrillFuel").result()) + .set("dwarvenMines", dynamic.emptyMap() + .setFieldIfPresent("solveFetchur", dwarvenMines.get("solveFetchur").result()) + .setFieldIfPresent("solvePuzzler", dwarvenMines.get("solvePuzzler").result()) + ) + .set("dwarvenHud", dwarvenMines.get("dwarvenHud").result().orElseThrow() + .renameField("x", "commissionsX") + .renameField("y", "commissionsY") + ) + .setFieldIfPresent("crystalsHud", dwarvenMines.get("crystalsHud").result()) + .setFieldIfPresent("crystalsWaypoints", dwarvenMines.get("crystalsWaypoints").result()) + .set("crystalHollows", dynamic.emptyMap() + .setFieldIfPresent("metalDetectorHelper", dwarvenMines.get("metalDetectorHelper").result()) + ) + ).update("locations", locations -> locations.remove("dwarvenMines")); + } + + private static <T> Dynamic<T> fixFarming(Dynamic<T> dynamic) { + return dynamic.set("farming", dynamic.emptyMap() + .setFieldIfPresent("garden", dynamic.get("locations").get("garden").result()) + ).update("locations", locations -> locations.remove("garden")); + } + + private static <T> Dynamic<T> fixOtherLocations(Dynamic<T> dynamic) { + return dynamic.renameField("locations", "otherLocations"); + } + + private static <T> Dynamic<T> fixSlayers(Dynamic<T> dynamic) { + return dynamic.renameField("slayer", "slayers"); + } + + private static <T> Dynamic<T> fixChat(Dynamic<T> dynamic) { + return dynamic.renameField("messages", "chat"); + } + + private static <T> Dynamic<T> fixQuickNav(Dynamic<T> dynamic) { + return dynamic.update("quickNav", quickNav -> quickNav.updateMapValues(button -> + button.getFirst().asString().getOrThrow().startsWith("button") ? button.mapSecond(ConfigFix1::fixQuickNavButton) : button + )); + } + + private static <T> Dynamic<T> fixQuickNavButton(Dynamic<T> button) { + return button.update("item", item -> item + .renameField("itemName", "id") + .renameAndFixField("nbt", "components", nbt -> fixNbt(item.get("itemName"), nbt)) + ); + } + + private static Dynamic<?> fixNbt(OptionalDynamic<?> id, Dynamic<?> nbt) { + try { + String itemNbt = "{id:\"minecraft:" + id.asString().getOrThrow().toLowerCase(Locale.ROOT) + "\",Count:1"; + String extraNbt = nbt.asString().getOrThrow(); + if (extraNbt.length() > 2) itemNbt += "," + extraNbt; + itemNbt += "}"; + + ItemStack fixed = ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse(itemNbt)); + + return nbt.createString(ItemStackComponentizationFixer.componentsAsString(fixed)); + } catch (Exception e) { + ConfigDataFixer.LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Config Data Fixer] Failed to convert nbt to components!", e); + } + + return nbt.createString("[]"); + } + + private static <T> Dynamic<T> fixMisc(Dynamic<T> dynamic) { + return dynamic.set("misc", dynamic.emptyMap() + .setFieldIfPresent("richPresence", dynamic.get("richPresence").result()) + ).remove("richPresence"); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix2QuickNav.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix2QuickNav.java new file mode 100644 index 00000000..bd67b1b0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigFix2QuickNav.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.config.datafixer; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.TypeRewriteRule; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.serialization.Dynamic; +import net.minecraft.util.Identifier; + +public class ConfigFix2QuickNav extends ConfigDataFix { + public ConfigFix2QuickNav(Schema outputSchema, boolean changesType) { + super(outputSchema, changesType); + } + + @Override + protected TypeRewriteRule makeRule() { + return fixTypeEverywhereTyped( + "ConfigFix2QuickNav", + getInputSchema().getType(ConfigDataFixer.CONFIG_TYPE), + typed -> typed.update(DSL.remainderFinder(), this::fix) + ); + } + + private <T> Dynamic<T> fix(Dynamic<T> dynamic) { + return fixVersion(dynamic).update("quickNav", quickNav -> quickNav + .renameField("button12", "button13") + .renameField("button11", "button12") + .renameField("button10", "button11") + .renameField("button9", "button10") + .renameField("button8", "button9") + .renameField("button7", "button8") + .updateMapValues(button -> button.getFirst().asString().getOrThrow().startsWith("button") ? button.mapSecond(this::fixButton) : button) + ); + } + + private <T> Dynamic<T> fixButton(Dynamic<T> button) { + return button.renameAndFixField("item", "itemData", itemData -> itemData.renameAndFixField("id", "item", id -> id.createString(new Identifier(id.asString().getOrThrow()).toString()))); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java new file mode 100644 index 00000000..4c821169 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/datafixer/ConfigSchema.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.config.datafixer; + +import com.mojang.datafixers.DSL; +import com.mojang.datafixers.schemas.Schema; +import com.mojang.datafixers.types.templates.TypeTemplate; + +import java.util.Map; +import java.util.function.Supplier; + +public class ConfigSchema extends Schema { + public ConfigSchema(int versionKey, Schema parent) { + super(versionKey, parent); + } + + @Override + public void registerTypes(Schema schema, Map<String, Supplier<TypeTemplate>> entityTypes, Map<String, Supplier<TypeTemplate>> blockEntityTypes) { + schema.registerType(true, ConfigDataFixer.CONFIG_TYPE, DSL::remainder); + } + + @Override + public Map<String, Supplier<TypeTemplate>> registerEntities(Schema schema) { + return Map.of(); + } + + @Override + public Map<String, Supplier<TypeTemplate>> registerBlockEntities(Schema schema) { + return Map.of(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/debug/Debug.java b/src/main/java/de/hysky/skyblocker/debug/Debug.java index 8c883b30..d9ac668c 100644 --- a/src/main/java/de/hysky/skyblocker/debug/Debug.java +++ b/src/main/java/de/hysky/skyblocker/debug/Debug.java @@ -2,21 +2,27 @@ package de.hysky.skyblocker.debug; import com.mojang.brigadier.Command; import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.serialization.JsonOps; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.ItemUtils; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.item.ItemStack; import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.text.Text; - -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; +import org.lwjgl.glfw.GLFW; import java.util.List; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + public class Debug { private static final boolean DEBUG_ENABLED = Boolean.parseBoolean(System.getProperty("skyblocker.debug", "false")); @@ -26,6 +32,10 @@ public class Debug { return DEBUG_ENABLED || FabricLoader.getInstance().isDevelopmentEnvironment(); } + public static boolean shouldShowInvisibleArmorStands() { + return showInvisibleArmorStands; + } + public static void init() { if (debugEnabled()) { SnapshotDebug.init(); @@ -35,6 +45,15 @@ public class Debug { .then(toggleShowingInvisibleArmorStands()) .then(dumpArmorStandHeadTextures()) ))); + ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (screen instanceof HandledScreen<?> handledScreen) { + ScreenKeyboardEvents.afterKeyPress(screen).register((_screen, key, scancode, modifier) -> { + if (key == GLFW.GLFW_KEY_U && client.player != null) { + client.player.sendMessage(Text.literal("[Skyblocker Debug] Hovered Item: " + SkyblockerMod.GSON_COMPACT.toJson(ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, ((HandledScreenAccessor) handledScreen).getFocusedSlot().getStack())))); + } + }); + } + }); } } @@ -73,8 +92,4 @@ public class Debug { return Command.SINGLE_SUCCESS; }); } - - public static boolean shouldShowInvisibleArmorStands() { - return showInvisibleArmorStands; - } } diff --git a/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java b/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java index 303e454f..c268103d 100644 --- a/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java +++ b/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java @@ -1,5 +1,6 @@ package de.hysky.skyblocker.events; +import de.hysky.skyblocker.utils.Location; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.event.Event; @@ -19,6 +20,12 @@ public final class SkyblockEvents { } }); + public static final Event<SkyblockEvents.SkyblockLocationChange> LOCATION_CHANGE = EventFactory.createArrayBacked(SkyblockEvents.SkyblockLocationChange.class, callbacks -> location -> { + for (SkyblockEvents.SkyblockLocationChange callback : callbacks) { + callback.onSkyblockLocationChange(location); + } + }); + @Environment(EnvType.CLIENT) @FunctionalInterface public interface SkyblockJoin { @@ -30,4 +37,10 @@ public final class SkyblockEvents { public interface SkyblockLeave { void onSkyblockLeave(); } + + @Environment(EnvType.CLIENT) + @FunctionalInterface + public interface SkyblockLocationChange { + void onSkyblockLocationChange(Location location); + } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 0a5ebc64..2c2c1376 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -3,6 +3,8 @@ package de.hysky.skyblocker.mixins; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.CompactDamage; import de.hysky.skyblocker.skyblock.FishingHelper; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; @@ -18,12 +20,11 @@ import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityStatuses; import net.minecraft.entity.ItemEntity; -import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket; -import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket; -import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; -import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.network.packet.s2c.play.*; import net.minecraft.util.Identifier; import org.slf4j.Logger; +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; @@ -36,6 +37,10 @@ public abstract class ClientPlayNetworkHandlerMixin { @Shadow private ClientWorld world; + @Shadow + @Final + private static Logger LOGGER; + @Inject(method = "onBlockUpdate", at = @At("RETURN")) private void skyblocker$onBlockUpdate(BlockUpdateS2CPacket packet, CallbackInfo ci) { if (Utils.isInTheEnd() && SlayerUtils.isInSlayer()) { @@ -103,4 +108,14 @@ public abstract class ClientPlayNetworkHandlerMixin { } return entity; } + + @Inject(method = "onEntityTrackerUpdate", at = @At("TAIL")) + private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { + if (!SkyblockerConfigManager.get().uiAndVisuals.compactDamage.enabled || !(entity instanceof ArmorStandEntity armorStandEntity)) return; + try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers + CompactDamage.compactDamage(armorStandEntity); + } catch (Exception e) { + LOGGER.error("[Skyblocker Compact Damage] Failed to compact damage number", e); + } + } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/EntityRenderDispatcherMixin.java b/src/main/java/de/hysky/skyblocker/mixins/EntityRenderDispatcherMixin.java index 79d13068..c03a3a55 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/EntityRenderDispatcherMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/EntityRenderDispatcherMixin.java @@ -1,18 +1,34 @@ package de.hysky.skyblocker.mixins; import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.debug.Debug; +import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.decoration.ArmorStandEntity; + import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @Mixin(EntityRenderDispatcher.class) public class EntityRenderDispatcherMixin { - @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isInvisible()Z", ordinal = 1)) - private <E extends Entity> boolean skyblocker$armorStandHitboxVisible(boolean invisible, E entity) { - return (!(entity instanceof ArmorStandEntity) || !Utils.isOnHypixel() || !Debug.debugEnabled() || !Debug.shouldShowInvisibleArmorStands()) && invisible; - } + @Unique + private static final String SOULWEAVER_SKULL_TEXTURE = "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmYyNGVkNjg3NTMwNGZhNGExZjBjNzg1YjJjYjZhNmE3MjU2M2U5ZjNlMjRlYTU1ZTE4MTc4NDUyMTE5YWE2NiJ9fX0="; + + @ModifyReturnValue(method = "shouldRender", at = @At("RETURN")) + private <E extends Entity> boolean skyblocker$dontRenderSoulweaverSkulls(boolean original, @Local(argsOnly = true) E entity) { + return Utils.isInDungeons() && SkyblockerConfigManager.get().dungeons.hideSoulweaverSkulls && entity instanceof ArmorStandEntity armorStand && entity.isInvisible() && armorStand.hasStackEquipped(EquipmentSlot.HEAD) ? !ItemUtils.getHeadTexture(armorStand.getEquippedStack(EquipmentSlot.HEAD)).equals(SOULWEAVER_SKULL_TEXTURE) : original; + } + + @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;isInvisible()Z", ordinal = 1)) + private <E extends Entity> boolean skyblocker$armorStandHitboxVisible(boolean invisible, E entity) { + return (!(entity instanceof ArmorStandEntity) || !Utils.isOnHypixel() || !Debug.debugEnabled() || !Debug.shouldShowInvisibleArmorStands()) && invisible; + } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index 04e6749c..f662be7c 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java @@ -14,6 +14,8 @@ import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds; import de.hysky.skyblocker.skyblock.item.WikiLookup; import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview; import de.hysky.skyblocker.skyblock.item.tooltip.CompactorDeletorPreview; +import de.hysky.skyblocker.skyblock.quicknav.QuickNav; +import de.hysky.skyblocker.skyblock.quicknav.QuickNavButton; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; @@ -41,6 +43,7 @@ import org.spongepowered.asm.mixin.injection.ModifyVariable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; @@ -89,10 +92,22 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen @Final protected T handler; + @Unique + private List<QuickNavButton> quickNavButtons; + protected HandledScreenMixin(Text title) { super(title); } + @Inject(method = "init", at = @At("RETURN")) + private void skyblocker$initQuickNav(CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().quickNav.enableQuickNav && client != null && client.player != null && !client.player.isCreative()) { + for (QuickNavButton quickNavButton : quickNavButtons = QuickNav.init(getTitle().getString().trim())) { + addSelectableChild(quickNavButton); + } + } + } + @Inject(at = @At("HEAD"), method = "keyPressed") public void skyblocker$keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable<Boolean> cir) { if (this.client != null && this.focusedSlot != null && keyCode != 256) { @@ -109,8 +124,33 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen @Inject(at = @At("HEAD"), method = "mouseClicked") public void skyblocker$mouseClicked(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) { - if (SkyblockerConfigManager.get().farming.garden.visitorHelper && (Utils.getLocationRaw().equals("garden") && !getTitle().getString().contains("Logbook") || getTitle().getString().startsWith("Bazaar"))) + if (SkyblockerConfigManager.get().farming.garden.visitorHelper && (Utils.getLocationRaw().equals("garden") && !getTitle().getString().contains("Logbook") || getTitle().getString().startsWith("Bazaar"))) { VisitorHelper.onMouseClicked(mouseX, mouseY, button, this.textRenderer); + } + } + + /** + * Draws the unselected tabs in front of the background blur, but behind the main inventory, similar to creative inventory tabs + */ + @Inject(method = "renderBackground", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/gui/DrawContext;FII)V")) + private void skyblocker$drawUnselectedQuickNavButtons(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (quickNavButtons != null) for (QuickNavButton quickNavButton : quickNavButtons) { + if (!quickNavButton.toggled()) { + quickNavButton.render(context, mouseX, mouseY, delta); + } + } + } + + /** + * Draws the selected tab in front of the background blur and the main inventory, similar to creative inventory tabs + */ + @Inject(method = "renderBackground", at = @At("RETURN")) + private void skyblocker$drawSelectedQuickNavButtons(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + if (quickNavButtons != null) for (QuickNavButton quickNavButton : quickNavButtons) { + if (quickNavButton.toggled()) { + quickNavButton.render(context, mouseX, mouseY, delta); + } + } } @SuppressWarnings("DataFlowIssue") @@ -165,7 +205,7 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen /** * The naming of this method in yarn is half true, its mostly to handle slot/item interactions (which are mouse or keyboard clicks) * For example, using the drop key bind while hovering over an item will invoke this method to drop the players item - * + * * @implNote This runs before {@link ScreenHandler#onSlotClick(int, int, SlotActionType, net.minecraft.entity.player.PlayerEntity)} */ @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"), cancellable = true) @@ -216,7 +256,9 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen } if (currentSolver != null) { - SkyblockerMod.getInstance().containerSolverManager.onSlotClick(slotId, stack); + boolean disallowed = SkyblockerMod.getInstance().containerSolverManager.onSlotClick(slotId, stack); + + if (disallowed) ci.cancel(); } // Experiment Solvers diff --git a/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java new file mode 100644 index 00000000..6706db58 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/SignEditScreenMixin.java @@ -0,0 +1,44 @@ +package de.hysky.skyblocker.mixins; + + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.calculators.SignCalculator; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.AbstractSignEditScreen; +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; + +import java.util.Objects; + +@Mixin(AbstractSignEditScreen.class) +public abstract class SignEditScreenMixin { + @Shadow + @Final + private String[] messages; + + @Inject(method = "render", at = @At("HEAD")) + private void skyblocker$render(DrawContext context, int mouseX, int mouseY, float delta, CallbackInfo ci) { + //if the sign is being used to enter number send it to the sign calculator + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.enabled && Objects.equals(messages[1], "^^^^^^^^^^^^^^^")) { + SignCalculator.renderCalculator(context, messages[0], context.getScaledWindowWidth() / 2, 55); + } + } + + @Inject(method = "finishEditing", at = @At("HEAD")) + private void skyblocker$finishEditing(CallbackInfo ci) { + //if the sign is being used to enter number get number from calculator for if maths has been done + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.enabled && Objects.equals(messages[1], "^^^^^^^^^^^^^^^")) { + boolean isPrice = messages[2].contains("price"); + String value = SignCalculator.getNewValue(isPrice); + if (value.length() >= 15) { + value = value.substring(0, 15); + } + messages[0] = value; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/accessors/HandledScreenAccessor.java b/src/main/java/de/hysky/skyblocker/mixins/accessors/HandledScreenAccessor.java index 447031d4..9a2a8311 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/accessors/HandledScreenAccessor.java +++ b/src/main/java/de/hysky/skyblocker/mixins/accessors/HandledScreenAccessor.java @@ -2,6 +2,7 @@ package de.hysky.skyblocker.mixins.accessors; import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @@ -23,4 +24,7 @@ public interface HandledScreenAccessor { @Mutable @Accessor("handler") void setHandler(ScreenHandler handler); + + @Accessor("focusedSlot") + Slot getFocusedSlot(); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java b/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java new file mode 100644 index 00000000..2837364b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java @@ -0,0 +1,86 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.CustomArmorAnimatedDyes; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.text.TextColor; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.math.NumberUtils; + +import java.util.List; +import java.util.regex.Pattern; + + +public class CompactDamage { + private static final Pattern DAMAGE_PATTERN = Pattern.compile("[✧✯]?[\\d,]+[✧✯]?❤?"); + private CompactDamage() { + } + + public static void compactDamage(ArmorStandEntity entity) { + if (!entity.isInvisible() || !entity.hasCustomName() || !entity.isCustomNameVisible()) return; + Text customName = entity.getCustomName(); + String customNameStringified = customName.getString(); + if (!DAMAGE_PATTERN.matcher(customNameStringified).matches()) return; + List<Text> siblings = customName.getSiblings(); + if (siblings.isEmpty()) return; + + MutableText prettierCustomName; + if (siblings.size() == 1) { //Non-crit damage + Text text = siblings.getFirst(); + String dmg = text.getString().replace(",", ""); + if (!NumberUtils.isParsable(dmg)) return; //Sanity check + String prettifiedDmg = prettifyDamageNumber(Long.parseLong(dmg)); + int color; + if (text.getStyle().getColor() != null) { + if (text.getStyle().getColor() == TextColor.fromFormatting(Formatting.GRAY)) { + color = SkyblockerConfigManager.get().uiAndVisuals.compactDamage.normalDamageColor.getRGB() & 0x00FFFFFF; + } else color = text.getStyle().getColor().getRgb(); + } else color = SkyblockerConfigManager.get().uiAndVisuals.compactDamage.normalDamageColor.getRGB() & 0x00FFFFFF; + prettierCustomName = Text.literal("").append(Text.literal(prettifiedDmg).setStyle(customName.getStyle()).withColor(color)); + } else { //Crit damage + boolean wasDoubled = customNameStringified.contains("❤"); //Ring of love ability adds a heart to the end of the damage string + int entriesToRemove = wasDoubled ? 2 : 1; + + String dmg = siblings.subList(1, siblings.size() - entriesToRemove) //First and last sibling are the crit symbols and maybe heart + .stream() + .map(Text::getString) + .reduce("", String::concat) //Concatenate all the siblings to get the dmg number + .replace(",", ""); + + if (!NumberUtils.isParsable(dmg)) return; //Sanity check + String dmgSymbol = customNameStringified.charAt(0) != '✯' ? "✧" : "✯"; //Mega Crit ability from the Overload enchantment + String prettifiedDmg = dmgSymbol + prettifyDamageNumber(Long.parseLong(dmg)) + dmgSymbol; + prettierCustomName = Text.literal(""); + int length = prettifiedDmg.length(); + for (int i = 0; i < length; i++) { + prettierCustomName.append(Text.literal(prettifiedDmg.substring(i, i + 1)).withColor( + CustomArmorAnimatedDyes.interpolate( + SkyblockerConfigManager.get().uiAndVisuals.compactDamage.critDamageGradientStart.getRGB() & 0x00FFFFFF, + SkyblockerConfigManager.get().uiAndVisuals.compactDamage.critDamageGradientEnd.getRGB() & 0x00FFFFFF, + i / (length - 1.0) + ) + )); + } + + if (wasDoubled) prettierCustomName.append(Text.literal("❤").formatted(Formatting.LIGHT_PURPLE)); + + prettierCustomName.setStyle(customName.getStyle()); + } + + entity.setCustomName(prettierCustomName); + } + + private static String prettifyDamageNumber(long damage) { + if (damage < 1_000) return String.valueOf(damage); + if (damage < 1_000_000) return format(damage / 1_000.0) + "k"; + if (damage < 1_000_000_000) return format(damage / 1_000_000.0) + "M"; + if (damage < 1_000_000_000_000L) return format(damage / 1_000_000_000.0) + "B"; + return format(damage / 1_000_000_000_000.0) + "T"; //This will probably never be reached + } + + private static String format(double number) { + return ("%." + SkyblockerConfigManager.get().uiAndVisuals.compactDamage.precision + "f").formatted(number); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java index af931eb1..f3db2a25 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/AuctionViewScreen.java @@ -5,6 +5,7 @@ import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.render.gui.AbstractCustomHypixelGUI; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.screen.PopupScreen; +import net.minecraft.client.gui.tooltip.Tooltip; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.DirectionalLayoutWidget; import net.minecraft.client.gui.widget.SimplePositioningWidget; @@ -21,7 +22,9 @@ import net.minecraft.text.TextColor; import net.minecraft.util.Colors; import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; +import org.lwjgl.glfw.GLFW; +import java.time.Duration; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -56,6 +59,15 @@ public class AuctionViewScreen extends AbstractCustomHypixelGUI<AuctionHouseScre } @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_ESCAPE) { + clickSlot(BACK_BUTTON_SLOT); + return true; + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override protected void init() { super.init(); verticalLayout.spacing(2).getMainPositioner().alignHorizontalCenter(); @@ -78,10 +90,13 @@ public class AuctionViewScreen extends AbstractCustomHypixelGUI<AuctionHouseScre verticalLayout.forEachChild(this::addDrawableChild); updateLayout(); - addDrawableChild(new ButtonWidget.Builder(Text.literal("<"), button -> this.clickSlot(BACK_BUTTON_SLOT)) + ButtonWidget backButton = new ButtonWidget.Builder(Text.literal("<"), button -> this.clickSlot(BACK_BUTTON_SLOT)) .position(x + backgroundWidth - 16, y + 4) .size(12, 12) - .build()); + .tooltip(Tooltip.of(Text.literal("or press ESC!"))) + .build(); + backButton.setTooltipDelay(Duration.ofSeconds(1)); + addDrawableChild(backButton); } @@ -189,7 +204,7 @@ public class AuctionViewScreen extends AbstractCustomHypixelGUI<AuctionHouseScre @Override public void onSlotChange(AuctionHouseScreenHandler handler, int slotId, ItemStack stack) { - if (stack.isOf(Items.BLACK_STAINED_GLASS_PANE) || slotId == 13) return; + if (stack.isOf(Items.BLACK_STAINED_GLASS_PANE) || slotId == 13 || slotId >= handler.getRows() * 9) return; assert client != null; if (stack.isOf(Items.RED_TERRACOTTA)) { // Red terracotta shows up when you can cancel it changeState(BuyState.CANCELLABLE_AUCTION); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java b/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java index 9d460803..f96e3231 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/auction/EditBidPopup.java @@ -1,5 +1,7 @@ package de.hysky.skyblocker.skyblock.auction; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.calculators.SignCalculator; import de.hysky.skyblocker.utils.render.gui.AbstractPopupScreen; import net.minecraft.block.entity.SignBlockEntity; import net.minecraft.client.MinecraftClient; @@ -9,7 +11,6 @@ import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket; import net.minecraft.text.Style; import net.minecraft.text.Text; import org.jetbrains.annotations.NotNull; -import org.lwjgl.glfw.GLFW; public class EditBidPopup extends AbstractPopupScreen { private DirectionalLayoutWidget layout = DirectionalLayoutWidget.vertical(); @@ -55,6 +56,9 @@ public class EditBidPopup extends AbstractPopupScreen { public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) { super.renderBackground(context, mouseX, mouseY, delta); drawPopupBackground(context, layout.getX(), layout.getY(), layout.getWidth(), layout.getHeight()); + if (SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.enabled) { + SignCalculator.renderCalculator(context, textFieldWidget.getText(), context.getScaledWindowWidth() / 2, textFieldWidget.getY() - 8); + } } private boolean isStringGood(String s) { @@ -69,8 +73,13 @@ public class EditBidPopup extends AbstractPopupScreen { } private void done(ButtonWidget widget) { - if (!isStringGood(textFieldWidget.getText().trim())) return; - sendPacket(textFieldWidget.getText().trim()); + if (SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.enabled) { + if (!isStringGood(SignCalculator.getNewValue(false))) return; + sendPacket(SignCalculator.getNewValue(false)); + } else { + if (!isStringGood(textFieldWidget.getText().trim())) return; + sendPacket(textFieldWidget.getText().trim()); + } this.close(); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/calculators/CalculatorCommand.java b/src/main/java/de/hysky/skyblocker/skyblock/calculators/CalculatorCommand.java new file mode 100644 index 00000000..d103bcdd --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/calculators/CalculatorCommand.java @@ -0,0 +1,56 @@ +package de.hysky.skyblocker.skyblock.calculators; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.Calculator; +import de.hysky.skyblocker.utils.Constants; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.text.NumberFormat; + +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class CalculatorCommand { + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final NumberFormat FORMATTER = NumberFormat.getInstance(); + + public static void init() { + ClientCommandRegistrationCallback.EVENT.register(CalculatorCommand::calculate); + } + + private static void calculate(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(literal(SkyblockerMod.NAMESPACE) + .then(literal("calculate") + .then(argument("equation", StringArgumentType.greedyString()) + .executes(context -> doCalculation(getString(context, "equation"))) + ) + ) + ); + } + + private static int doCalculation(String calculation) { + MutableText text = Constants.PREFIX.get(); + try { + text.append(Text.literal(FORMATTER.format(Calculator.calculate(calculation))).formatted(Formatting.GREEN)); + } catch (UnsupportedOperationException e) { + text.append(Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator.invalidEquation").formatted(Formatting.RED)); + } + + if (CLIENT == null || CLIENT.player == null) { + return 0; + } + + CLIENT.player.sendMessage(text, false); + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/calculators/SignCalculator.java b/src/main/java/de/hysky/skyblocker/skyblock/calculators/SignCalculator.java new file mode 100644 index 00000000..dc51e48c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/calculators/SignCalculator.java @@ -0,0 +1,66 @@ +package de.hysky.skyblocker.skyblock.calculators; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Calculator; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.text.NumberFormat; + +public class SignCalculator { + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final NumberFormat FORMATTER = NumberFormat.getInstance(); + + private static String lastInput; + private static double output; + + public static void renderCalculator(DrawContext context, String message, int renderX, int renderY) { + if (SkyblockerConfigManager.get().uiAndVisuals.inputCalculator.requiresEquals && !message.startsWith("=")) { + output = -1; + lastInput = message; + return; + } + if (message.startsWith("=")) { + message = message.substring(1); + } + //only update output if new input + if (!message.equals(lastInput)) { // + try { + output = Calculator.calculate(message); + } catch (Exception e) { + output = -1; + } + } + + render(context, message, renderX, renderY); + + lastInput = message; + } + + public static String getNewValue(Boolean isPrice) { + if (output == -1) { + //if mode is not activated or just invalid equation return what the user typed in + return lastInput; + } + + //price can except decimals and exponents + if (isPrice) { + return FORMATTER.format(output); + } + //amounts want an integer number so round + return Long.toString(Math.round(output)); + } + + private static void render(DrawContext context, String input, int renderX, int renderY) { + Text text; + if (output == -1) { + text = Text.translatable("skyblocker.config.uiAndVisuals.inputCalculator.invalidEquation").formatted(Formatting.RED); + } else { + text = Text.literal(input + " = " + FORMATTER.format(output)).formatted(Formatting.GREEN); + } + + context.drawCenteredTextWithShadow(CLIENT.textRenderer, text, renderX, renderY, 0xFFFFFFFF); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java index 01673f23..833e85a3 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java @@ -16,7 +16,7 @@ import org.slf4j.LoggerFactory; import java.util.*; -public class ColorTerminal extends ContainerSolver { +public class ColorTerminal extends ContainerSolver implements TerminalSolver { private static final Logger LOGGER = LoggerFactory.getLogger(ColorTerminal.class.getName()); private static final Map<String, DyeColor> colorFromName; private DyeColor targetColor; @@ -53,6 +53,14 @@ public class ColorTerminal extends ContainerSolver { return highlights; } + @Override + protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { + if (stack.hasGlint() || !targetColor.equals(itemColor.get(stack.getItem()))) { + return shouldBlockIncorrectClicks(); + } + + return false; + } static { colorFromName = new HashMap<>(); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/LightsOnTerminal.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/LightsOnTerminal.java new file mode 100644 index 00000000..67b51c22 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/LightsOnTerminal.java @@ -0,0 +1,37 @@ +package de.hysky.skyblocker.skyblock.dungeon.terminal; + +import java.util.List; + +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +/** + * The terminal where you change all the panes that are red to green. + * + * This doesn't solve the terminal because you don't need a solver for it, but rather to simply allow for click blocking. + */ +public class LightsOnTerminal extends ContainerSolver implements TerminalSolver { + private static final List<ColorHighlight> EMPTY = List.of(); + + public LightsOnTerminal() { + super("^Correct all the panes!$"); + } + + @Override + protected boolean isEnabled() { + return shouldBlockIncorrectClicks(); + } + + @Override + protected List<ColorHighlight> getColors(String[] groups, Int2ObjectMap<ItemStack> slots) { + return EMPTY; + } + + @Override + protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { + return stack.isOf(Items.LIME_STAINED_GLASS_PANE); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java index d8d9a63a..e980a136 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java @@ -11,7 +11,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class OrderTerminal extends ContainerSolver { +public class OrderTerminal extends ContainerSolver implements TerminalSolver { private final int PANES_NUM = 14; private int[] orderedSlots; private int currentNum = Integer.MAX_VALUE; @@ -55,4 +55,15 @@ public class OrderTerminal extends ContainerSolver { currentNum = 0; return true; } + + @Override + protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { + if (stack == null || stack.isEmpty()) return false; + + if (!stack.isOf(Items.RED_STAINED_GLASS_PANE) || stack.getCount() != currentNum + 1) { + return shouldBlockIncorrectClicks(); + } + + return false; + } }
\ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java index c4cc8e47..51a778a5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java @@ -13,7 +13,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; -public class StartsWithTerminal extends ContainerSolver { +public class StartsWithTerminal extends ContainerSolver implements TerminalSolver { private final Int2ObjectOpenHashMap<ItemState> trackedItemStates = new Int2ObjectOpenHashMap<>(); private int lastKnownScreenId = Integer.MIN_VALUE; @@ -50,9 +50,9 @@ public class StartsWithTerminal extends ContainerSolver { } @Override - protected void onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { + protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { //Some random glass pane was clicked or something - if (!trackedItemStates.containsKey(slot) || stack == null || stack.isEmpty()) return; + if (!trackedItemStates.containsKey(slot) || stack == null || stack.isEmpty()) return false; ItemState state = trackedItemStates.get(slot); String prefix = groups[0]; @@ -61,16 +61,17 @@ public class StartsWithTerminal extends ContainerSolver { //Also, since Hypixel closes & reopens the GUI after every click we check if the last known screen id is the same that way in case the server lags and //either a player tries to click a second item or if the player puts the clicked item back and tries to click another that we don't mark multiple items //as clicked when only the first one will count. - + //While Hypixel does use a different syncId each time they open the screen we opt to use our own so as to avoid them potentially changing that //and in turn breaking this logic if (stack.getName().getString().startsWith(prefix) && !state.clicked() && lastKnownScreenId != screenId) { trackedItemStates.put(slot, state.click()); lastKnownScreenId = screenId; + } else { + return shouldBlockIncorrectClicks(); } - //In the future we could add an else branch and return a boolean to cancel the click since it would be wrong - return; + return false; } //We only setup the state when all items aren't null or empty. This prevents the state from being reset due to unsent items or server lag spikes/bad TPS (fix ur servers Hypixel) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/TerminalSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/TerminalSolver.java new file mode 100644 index 00000000..7a7cd6bb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/TerminalSolver.java @@ -0,0 +1,10 @@ +package de.hysky.skyblocker.skyblock.dungeon.terminal; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; + +public interface TerminalSolver { + + default boolean shouldBlockIncorrectClicks() { + return SkyblockerConfigManager.get().dungeons.terminals.blockIncorrectClicks; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CommissionLabels.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CommissionLabels.java new file mode 100644 index 00000000..a14c71f7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CommissionLabels.java @@ -0,0 +1,99 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.configs.MiningConfig; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.util.math.BlockPos; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class CommissionLabels { + + private static final Map<String, MiningLocationLabel.DwarvenCategory> DWARVEN_LOCATIONS = Arrays.stream(MiningLocationLabel.DwarvenCategory.values()).collect(Collectors.toMap(MiningLocationLabel.DwarvenCategory::toString, Function.identity())); + private static final List<MiningLocationLabel.DwarvenEmissaries> DWARVEN_EMISSARIES = Arrays.stream(MiningLocationLabel.DwarvenEmissaries.values()).toList(); + private static final Map<String, MiningLocationLabel.GlaciteCategory> GLACITE_LOCATIONS = Arrays.stream(MiningLocationLabel.GlaciteCategory.values()).collect(Collectors.toMap(MiningLocationLabel.GlaciteCategory::toString, Function.identity())); + + protected static List<MiningLocationLabel> activeWaypoints = new ArrayList<>(); + + public static void init() { + WorldRenderEvents.AFTER_TRANSLUCENT.register(CommissionLabels::render); + } + + /** + * update the activeWaypoints when there is a change in commissions + * + * @param newCommissions the new commissions to get the waypoints from + * @param completed if there is a commission completed + */ + protected static void update(List<String> newCommissions, boolean completed) { + MiningConfig.CommissionWaypointMode currentMode = SkyblockerConfigManager.get().mining.commissionWaypoints.mode; + if (currentMode == MiningConfig.CommissionWaypointMode.OFF) { + return; + } + activeWaypoints.clear(); + String location = Utils.getIslandArea().substring(2); + //find commission locations in glacite + if (location.equals("Dwarven Base Camp") || location.equals("Glacite Tunnels") || location.equals("Glacite Mineshafts") || location.equals("Glacite Lake")) { + if (currentMode != MiningConfig.CommissionWaypointMode.BOTH && currentMode != MiningConfig.CommissionWaypointMode.GLACITE) { + return; + } + + for (String commission : newCommissions) { + for (Map.Entry<String, MiningLocationLabel.GlaciteCategory> glaciteLocation : GLACITE_LOCATIONS.entrySet()) { + if (commission.contains(glaciteLocation.getKey())) { + MiningLocationLabel.GlaciteCategory category = glaciteLocation.getValue(); + for (BlockPos gemstoneLocation : category.getLocations()) { + activeWaypoints.add(new MiningLocationLabel(category, gemstoneLocation)); + } + } + } + } + //add base waypoint if enabled + if (SkyblockerConfigManager.get().mining.commissionWaypoints.showBaseCamp) { + activeWaypoints.add(new MiningLocationLabel(MiningLocationLabel.GlaciteCategory.CAMPFIRE, MiningLocationLabel.GlaciteCategory.CAMPFIRE.getLocations()[0])); + } + return; + } + //find commission locations in dwarven mines + if (currentMode != MiningConfig.CommissionWaypointMode.BOTH && currentMode != MiningConfig.CommissionWaypointMode.DWARVEN) { + return; + } + + for (String commission : newCommissions) { + for (Map.Entry<String, MiningLocationLabel.DwarvenCategory> dwarvenLocation : DWARVEN_LOCATIONS.entrySet()) { + if (commission.contains(dwarvenLocation.getKey())) { + MiningLocationLabel.DwarvenCategory category = dwarvenLocation.getValue(); + category.isTitanium = commission.contains("Titanium"); + activeWaypoints.add(new MiningLocationLabel(category, category.getLocation())); + } + } + } + //if there is a commission completed and enabled show emissary + if (SkyblockerConfigManager.get().mining.commissionWaypoints.showEmissary && completed) { + for (MiningLocationLabel.DwarvenEmissaries emissaries : DWARVEN_EMISSARIES) { + activeWaypoints.add(new MiningLocationLabel(emissaries, emissaries.getLocation())); + } + } + } + + /** + * render all the active waypoints + * + * @param context render context + */ + private static void render(WorldRenderContext context) { + if (!Utils.isInDwarvenMines() || SkyblockerConfigManager.get().mining.commissionWaypoints.mode == MiningConfig.CommissionWaypointMode.OFF) { + return; + } + for (MiningLocationLabel MiningLocationLabel : activeWaypoints) { + MiningLocationLabel.render(context); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java index a74dbc5e..63430489 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsHud.java @@ -21,7 +21,7 @@ import java.util.Map; public class CrystalsHud { private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - protected static final Identifier MAP_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/crystals_map.png"); + protected static final Identifier MAP_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/crystals_map.png"); private static final Identifier MAP_ICON = new Identifier("textures/map/decorations/player.png"); private static final List<String> SMALL_LOCATIONS = List.of("Fairy Grotto", "King Yolkar", "Corleone", "Odawa", "Key Guardian"); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java index 053f3536..dc40f82c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsWaypoint.java @@ -42,7 +42,7 @@ public class CrystalsWaypoint extends Waypoint { @Override public boolean shouldRender() { - return super.shouldRender() ; + return super.shouldRender(); } @Override @@ -84,7 +84,7 @@ public class CrystalsWaypoint extends Waypoint { private final String name; private final float[] colorComponents; - Category(String name,Color color) { + Category(String name, Color color) { this.name = name; this.color = color; this.colorComponents = color.getColorComponents(null); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java index 242c513a..2cf0ea9d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java @@ -168,9 +168,9 @@ public class DwarvenHud { || !Utils.isInCrystalHollows() && !Utils.isInDwarvenMines()) { return; } - + List<String> oldCommissionNames = commissionList.stream().map(Commission::commission).toList(); + boolean oldCompleted = commissionList.stream().anyMatch(commission -> commission.progression.equals("DONE")); commissionList = new ArrayList<>(); - for (PlayerListEntry playerListEntry : CLIENT.getNetworkHandler().getPlayerList().stream().sorted(PlayerListHudAccessor.getOrdering()).toList()) { if (playerListEntry.getDisplayName() == null) { continue; @@ -197,6 +197,11 @@ public class DwarvenHud { glacitePowder = glaciteMatcher.group(0).split(": ")[1]; } } + List<String> newCommissionNames = commissionList.stream().map(Commission::commission).toList(); + boolean newCompleted = commissionList.stream().anyMatch(commission -> commission.progression.equals("DONE")); + if (!oldCommissionNames.equals(newCommissionNames) || oldCompleted != newCompleted) { + CommissionLabels.update(newCommissionNames, newCompleted); + } } // steamroller tactics to get visibility from outside classes (HudCommsWidget) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java new file mode 100644 index 00000000..1f373b55 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/MiningLocationLabel.java @@ -0,0 +1,157 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.Renderable; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +public record MiningLocationLabel(Category category, Vec3d centerPos) implements Renderable { + public MiningLocationLabel(Category category, BlockPos pos) { + this(category, pos.toCenterPos()); + } + + private Text getName() { + if (SkyblockerConfigManager.get().mining.commissionWaypoints.useColor) { + return Text.literal(category.getName()).withColor(category.getColor()); + } + return Text.literal(category.getName()); + } + + /** + * Renders the name and distance to the label scaled so can be seen at a distance + * @param context render context + */ + @Override + public void render(WorldRenderContext context) { + Vec3d posUp = centerPos.add(0, 1, 0); + double distance = context.camera().getPos().distanceTo(centerPos); + float scale = (float) (SkyblockerConfigManager.get().mining.commissionWaypoints.textScale * (distance / 10)); + RenderHelper.renderText(context, getName(), posUp, scale, true); + RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, scale, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true); + } + + public interface Category { + String getName(); + + int getColor(); //all the color codes are the color of the block the waypoint is for + } + + enum DwarvenCategory implements Category { + LAVA_SPRINGS("Lava Springs", new BlockPos(60, 197, -15)), + CLIFFSIDE_VEINS("Cliffside Veins", new BlockPos(40, 128, 40)), + RAMPARTS_QUARRY("Rampart's Quarry", new BlockPos(-100, 150, -20)), + UPPER_MINES("Upper Mines", new BlockPos(-130, 174, -50)), + ROYAL_MINES("Royal Mines", new BlockPos(130, 154, 30)), + GLACITE_WALKER("Glacite Walker", new BlockPos(0, 128, 150)); + + + boolean isTitanium; + private final String name; + private final BlockPos location; + + DwarvenCategory(String name, BlockPos location) { + this.name = name; + this.location = location; + } + + public BlockPos getLocation() { + return location; + } + + @Override + public String toString() { + return name; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getColor() { + if (isTitanium) { + return 0xd8d6d8; + } + return 0x45bde0; + } + } + + enum DwarvenEmissaries implements Category { + LAVA_SPRINGS(new BlockPos(58, 198, -8)), + CLIFFSIDE_VEINS(new BlockPos(42, 134, 22)), + RAMPARTS_QUARRY(new BlockPos(-72, 153, -10)), + UPPER_MINES(new BlockPos(-132, 174, -50)), + ROYAL_MINES(new BlockPos(171, 150, 31)), + DWARVEN_VILLAGE(new BlockPos(-37, 200, -92)), + DWARVEN_MINES(new BlockPos(89, 198, -92)); + + private final BlockPos location; + + DwarvenEmissaries(BlockPos location) { + + this.location = location; + } + + public BlockPos getLocation() { + return location; + } + + @Override + public String toString() { + return "Emissary"; + } + + @Override + public String getName() { + return "Emissary"; + } + + @Override + public int getColor() { + return 0xffffff; + } + } + + enum GlaciteCategory implements Category { + AQUAMARINE("Aquamarine", 0x334cb1, new BlockPos[]{new BlockPos(-1, 139, 437), new BlockPos(90, 151, 229), new BlockPos(56, 151, 400), new BlockPos(51, 117, 303)}), + ONYX("Onyx", 0x191919, new BlockPos[]{new BlockPos(79, 119, 411), new BlockPos(-14, 132, 386), new BlockPos(18, 136, 370), new BlockPos(16, 138, 411), new BlockPos(-68, 130, 408)}), + PERIDOT("Peridot", 0x667f33, new BlockPos[]{new BlockPos(-61, 147, 302), new BlockPos(91, 122, 397), new BlockPos(-73, 122, 458), new BlockPos(-77, 120, 282)}), + CITRINE("Citrine", 0x664c33, new BlockPos[]{new BlockPos(-104, 144, 244), new BlockPos(39, 119, 386), new BlockPos(-57, 144, 421), new BlockPos(-47, 126, 418)}), + CAMPFIRE("Base Camp", 0x983333, new BlockPos[]{new BlockPos(-7, 126, 229)}); + + private final String name; + private final int color; + private final BlockPos[] location; + + GlaciteCategory(String name, int color, BlockPos[] location) { + this.name = name; + this.color = color; + this.location = location; + } + + public BlockPos[] getLocations() { + return location; + } + + @Override + public String toString() { + return name; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getColor() { + return color; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/foraging/ModernForagingIsland.java b/src/main/java/de/hysky/skyblocker/skyblock/foraging/ModernForagingIsland.java new file mode 100644 index 00000000..fd256681 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/foraging/ModernForagingIsland.java @@ -0,0 +1,8 @@ +package de.hysky.skyblocker.skyblock.foraging; + +public class ModernForagingIsland { + + public static void init() { + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java index 4440cd84..76e7f02c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java @@ -109,7 +109,7 @@ public class CustomArmorAnimatedDyes { } //Credit to https://codepen.io/OliverBalfour/post/programmatically-making-gradients - private static int interpolate(int firstColor, int secondColor, double percentage) { + public static int interpolate(int firstColor, int secondColor, double percentage) { int r1 = MathHelper.square((firstColor >> 16) & 0xFF); int g1 = MathHelper.square((firstColor >> 8) & 0xFF); int b1 = MathHelper.square(firstColor & 0xFF); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index d2fda215..c6caaf41 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -40,6 +40,8 @@ public class ItemTooltip { public static void getTooltip(ItemStack stack, Item.TooltipContext tooltipContext, TooltipType tooltipType, List<Text> lines) { if (!Utils.isOnSkyblock() || client.player == null) return; + smoothenLines(lines); + String name = getInternalNameFromNBT(stack, false); String internalID = getInternalNameFromNBT(stack, true); String neuName = name; @@ -392,6 +394,15 @@ public class ItemTooltip { return message; } + private static void smoothenLines(List<Text> lines) { + for (int i = 0; i < lines.size(); i++) { + Text line = lines.get(i); + if (line.getString().equals("-----------------")) { + lines.set(i, Text.literal(" ").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH, Formatting.BOLD)); + } + } + } + // If these options is true beforehand, the client will get first data of these options while loading. // After then, it will only fetch the data if it is on Skyblock. public static int minute = 0; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java index 5529e466..5fa517cc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java @@ -1,17 +1,14 @@ package de.hysky.skyblocker.skyblock.quicknav; import com.mojang.brigadier.exceptions.CommandSyntaxException; - import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.QuickNavigationConfig; -import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.datafixer.ItemStackComponentizationFixer; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.fabricmc.fabric.api.client.screen.v1.Screens; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.nbt.StringNbtReader; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.slf4j.Logger; @@ -19,22 +16,11 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.regex.PatternSyntaxException; public class QuickNav { private static final Logger LOGGER = LoggerFactory.getLogger(QuickNav.class); - public static void init() { - ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().quickNav.enableQuickNav && screen instanceof HandledScreen<?> && client.player != null && !client.player.isCreative()) { - 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<>(); QuickNavigationConfig data = SkyblockerConfigManager.get().quickNav; @@ -51,6 +37,8 @@ public class QuickNav { if (data.button10.render) buttons.add(parseButton(data.button10, screenTitle, 9)); if (data.button11.render) buttons.add(parseButton(data.button11, screenTitle, 10)); if (data.button12.render) buttons.add(parseButton(data.button12, screenTitle, 11)); + if (data.button13.render) buttons.add(parseButton(data.button13, screenTitle, 12)); + if (data.button14.render) buttons.add(parseButton(data.button14, screenTitle, 13)); } catch (CommandSyntaxException e) { LOGGER.error("[Skyblocker] Failed to initialize Quick Nav Button", e); } @@ -58,23 +46,19 @@ public class QuickNav { } private static QuickNavButton parseButton(QuickNavigationConfig.QuickNavItem buttonInfo, String screenTitle, int id) throws CommandSyntaxException { - QuickNavigationConfig.ItemData itemData = buttonInfo.item; - String nbtString = "{id:\"minecraft:" + itemData.id.toLowerCase(Locale.ROOT) + "\",Count:1"; - if (itemData.nbt.length() > 2) nbtString += "," + itemData.nbt; - nbtString += "}"; + QuickNavigationConfig.ItemData itemData = buttonInfo.itemData; + ItemStack stack = itemData != null && itemData.item != null && itemData.components != null ? ItemStackComponentizationFixer.fromComponentsString(itemData.item.toString(), Math.clamp(itemData.count, 1, 99), itemData.components) : new ItemStack(Items.BARRIER); + boolean uiTitleMatches = false; try { uiTitleMatches = screenTitle.matches(buttonInfo.uiTitle); } catch (PatternSyntaxException e) { - LOGGER.error("[Skyblocker] Failed to parse Quick Nav Button", e); + LOGGER.error("[Skyblocker] Failed to parse Quick Nav Button with regex: {}", buttonInfo.uiTitle, e); ClientPlayerEntity player = MinecraftClient.getInstance().player; if (player != null) { - player.sendMessage(Text.of(Formatting.RED + "[Skyblocker] Invalid regex in quicknav button " + (id + 1) + "!"), false); + player.sendMessage(Constants.PREFIX.get().append(Text.literal("Invalid regex in Quick Nav Button " + (id + 1) + "!").formatted(Formatting.RED)), false); } } - return new QuickNavButton(id, - uiTitleMatches, - buttonInfo.clickEvent, - ItemStackComponentizationFixer.fixUpItem(StringNbtReader.parse(nbtString))); + return new QuickNavButton(id, uiTitleMatches, buttonInfo.clickEvent, stack); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java index 7db78590..f9ca0940 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java @@ -1,7 +1,6 @@ package de.hysky.skyblocker.skyblock.quicknav; import com.mojang.blaze3d.systems.RenderSystem; - import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.utils.scheduler.MessageScheduler; import net.fabricmc.api.EnvType; @@ -16,7 +15,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.text.Text; import net.minecraft.util.Identifier; -@Environment(value=EnvType.CLIENT) +@Environment(value = EnvType.CLIENT) public class QuickNavButton extends ClickableWidget { private final int index; private boolean toggled; @@ -25,18 +24,24 @@ public class QuickNavButton extends ClickableWidget { /** * Checks if the current tab is a top tab based on its index. - * @return true if the index is less than 6, false otherwise. + * + * @return true if the index is less than 7, false otherwise. */ private boolean isTopTab() { - return index < 6; + return index < 7; + } + + public boolean toggled() { + return toggled; } /** * Constructs a new QuickNavButton with the given parameters. - * @param index the index of the button. + * + * @param index the index of the button. * @param toggled the toggled state of the button. * @param command the command to execute when the button is clicked. - * @param icon the icon to display on the button. + * @param icon the icon to display on the button. */ public QuickNavButton(int index, boolean toggled, String command, ItemStack icon) { super(0, 0, 26, 32, Text.empty()); @@ -49,18 +54,18 @@ public class QuickNavButton extends ClickableWidget { private void updateCoordinates() { Screen screen = MinecraftClient.getInstance().currentScreen; if (screen instanceof HandledScreen<?> handledScreen) { - int x = ((HandledScreenAccessor)handledScreen).getX(); - int y = ((HandledScreenAccessor)handledScreen).getY(); - int h = ((HandledScreenAccessor)handledScreen).getBackgroundHeight(); - if (h > 166) --h; // why is this even a thing - this.setX(x + this.index % 6 * 26 + 4); - this.setY(this.index < 6 ? y - 26 : y + h - 4); + int x = ((HandledScreenAccessor) handledScreen).getX(); + int y = ((HandledScreenAccessor) handledScreen).getY(); + int h = ((HandledScreenAccessor) handledScreen).getBackgroundHeight(); + this.setX(x + this.index % 7 * 25); + this.setY(this.index < 7 ? y - 28 : y + h - 4); } } /** * Handles click events. If the button is not currently toggled, * it sets the toggled state to true and sends a message with the command after cooldown. + * * @param mouseX the x-coordinate of the mouse click * @param mouseY the y-coordinate of the mouse click */ @@ -78,9 +83,10 @@ public class QuickNavButton extends ClickableWidget { * The method first updates the coordinates of the button, * then calculates appropriate values for rendering based on its current state, * and finally draws both the background and icon of the button on screen. + * * @param context the context in which to render the button - * @param mouseX the x-coordinate of the mouse cursor - * @param mouseY the y-coordinate of the mouse cursor + * @param mouseX the x-coordinate of the mouse cursor + * @param mouseY the y-coordinate of the mouse cursor */ @Override public void renderWidget(DrawContext context, int mouseX, int mouseY, float delta) { @@ -88,31 +94,16 @@ public class QuickNavButton extends ClickableWidget { RenderSystem.disableDepthTest(); // Construct the texture identifier based on the index and toggled state - String tabType = isTopTab() ? "top" : "bottom"; - Identifier BUTTON_TEXTURES = new Identifier("container/creative_inventory/tab_" + tabType + - (toggled ? "_selected_" : "_unselected_") + 2); + Identifier tabTexture = new Identifier("container/creative_inventory/tab_" + (isTopTab() ? "top" : "bottom") + "_" + (toggled ? "selected" : "unselected") + "_" + (index % 7 + 1)); // Render the button texture - int y = this.getY(); - if (this.toggled) { - if (this.index < 6) y -= 2; - } else { - y += (this.index >= 6) ? 4 : -2; - } - int height = this.height - ((this.toggled ) ? 0 : 4); - - context.drawGuiTexture(BUTTON_TEXTURES, this.getX(), y, this.width, height); - + context.drawGuiTexture(tabTexture, this.getX(), this.getY(), this.width, this.height); // Render the button icon - int yOffset = !this.toggled && this.index < 6 ? 1 : 0; - context.drawItem(this.icon, this.getX() + 5, this.getY() + 6 + yOffset); - + int yOffset = this.index < 7 ? 1 : -1; + context.drawItem(this.icon, this.getX() + 5, this.getY() + 8 + yOffset); RenderSystem.enableDepthTest(); } - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - // TODO Auto-generated method stub - - } + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) {} } diff --git a/src/main/java/de/hysky/skyblocker/utils/Calculator.java b/src/main/java/de/hysky/skyblocker/utils/Calculator.java new file mode 100644 index 00000000..7b0baaf6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Calculator.java @@ -0,0 +1,208 @@ +package de.hysky.skyblocker.utils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Calculator { + public enum TokenType { + NUMBER, OPERATOR, L_PARENTHESIS, R_PARENTHESIS + } + + public static class Token { + public TokenType type; + String value; + int tokenLength; + } + + private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+\\.?\\d*)([sekmbt]?)"); + private static final Map<String, Long> MAGNITUDE_VALUES = Map.of( + "s", 64L, + "e", 160L, + "k", 1_000L, + "m", 1_000_000L, + "b", 1_000_000_000L, + "t", 1_000_000_000_000L + ); + + private static List<Token> lex(String input) { + List<Token> tokens = new ArrayList<>(); + input = input.replace(" ", "").toLowerCase().replace("x", "*"); + int i = 0; + while (i < input.length()) { + Token token = new Token(); + switch (input.charAt(i)) { + case '+', '-', '*', '/' -> { + token.type = TokenType.OPERATOR; + token.value = String.valueOf(input.charAt(i)); + token.tokenLength = 1; + } + + case '(' -> { + token.type = TokenType.L_PARENTHESIS; + token.value = String.valueOf(input.charAt(i)); + token.tokenLength = 1; + //add implicit multiplication when there is a number before brackets + if (!tokens.isEmpty()) { + TokenType lastType = tokens.getLast().type; + if (lastType == TokenType.R_PARENTHESIS || lastType == TokenType.NUMBER) { + Token mutliplyToken = new Token(); + mutliplyToken.type = TokenType.OPERATOR; + mutliplyToken.value = "*"; + tokens.add(mutliplyToken); + } + } + } + + case ')' -> { + token.type = TokenType.R_PARENTHESIS; + token.value = String.valueOf(input.charAt(i)); + token.tokenLength = 1; + } + + default -> { + token.type = TokenType.NUMBER; + Matcher numberMatcher = NUMBER_PATTERN.matcher(input.substring(i)); + if (!numberMatcher.find()) {//invalid value to lex + throw new UnsupportedOperationException("invalid character"); + } + int end = numberMatcher.end(); + token.value = input.substring(i, i + end); + token.tokenLength = end; + } + } + tokens.add(token); + + i += token.tokenLength; + } + + return tokens; + } + + /** + * This is an implementation of the shunting yard algorithm to convert the equation to reverse polish notation + * + * @param tokens equation in infix notation order + * @return equation in RPN order + */ + private static List<Token> shunt(List<Token> tokens) { + Deque<Token> operatorStack = new ArrayDeque<>(); + List<Token> outputQueue = new ArrayList<>(); + + for (Token shuntingToken : tokens) { + switch (shuntingToken.type) { + case NUMBER -> outputQueue.add(shuntingToken); + case OPERATOR -> { + int precedence = getPrecedence(shuntingToken.value); + while (!operatorStack.isEmpty()) { + Token leftToken = operatorStack.peek(); + if (leftToken.type == TokenType.L_PARENTHESIS) { + break; + } + assert (leftToken.type == TokenType.OPERATOR); + int leftPrecedence = getPrecedence(leftToken.value); + if (leftPrecedence >= precedence) { + outputQueue.add(operatorStack.pop()); + continue; + } + break; + } + operatorStack.push(shuntingToken); + } + case L_PARENTHESIS -> operatorStack.push(shuntingToken); + case R_PARENTHESIS -> { + while (true) { + if (operatorStack.isEmpty()) { + throw new UnsupportedOperationException("Unbalanced left parenthesis"); + } + Token leftToken = operatorStack.pop(); + if (leftToken.type == TokenType.L_PARENTHESIS) { + break; + } + outputQueue.add(leftToken); + } + } + } + } + //empty the operator stack + while (!operatorStack.isEmpty()) { + Token leftToken = operatorStack.pop(); + if (leftToken.type == TokenType.L_PARENTHESIS) { + //technically unbalanced left parenthesis error but just assume they are close after the equation and ignore them from here + continue; + } + outputQueue.add(leftToken); + } + + return outputQueue.stream().toList(); + } + + private static int getPrecedence(String operator) { + switch (operator) { + case "+", "-" -> { + return 0; + } + case "*", "/" -> { + return 1; + } + default -> throw new UnsupportedOperationException("Invalid operator"); + } + } + + /** + * @param tokens list of Tokens in reverse polish notation + * @return answer to equation + */ + private static double evaluate(List<Token> tokens) { + Deque<Double> values = new ArrayDeque<>(); + for (Token token : tokens) { + switch (token.type) { + case NUMBER -> values.push(calculateValue(token.value)); + case OPERATOR -> { + double right = values.pop(); + double left = values.pop(); + switch (token.value) { + case "+" -> values.push(left + right); + case "-" -> values.push(left - right); + case "/" -> { + if (right == 0) { + throw new UnsupportedOperationException("Can not divide by 0"); + } + values.push(left / right); + } + case "*" -> values.push(left * right); + } + } + case L_PARENTHESIS, R_PARENTHESIS -> throw new UnsupportedOperationException("Equation is not in RPN"); + } + } + if (values.isEmpty()) { + throw new UnsupportedOperationException("Equation is empty"); + } + return values.pop(); + } + + private static double calculateValue(String value) { + Matcher numberMatcher = NUMBER_PATTERN.matcher(value.toLowerCase()); + if (!numberMatcher.matches()) { + throw new UnsupportedOperationException("Invalid number"); + } + double number = Double.parseDouble(numberMatcher.group(1)); + String magnitude = numberMatcher.group(2); + + if (!magnitude.isEmpty()) { + if (!MAGNITUDE_VALUES.containsKey(magnitude)) {//its invalid if its another letter + throw new UnsupportedOperationException("Invalid magnitude"); + } + number *= MAGNITUDE_VALUES.get(magnitude); + } + + return number; + } + + public static double calculate(String equation) { + //custom bit for replacing purse with its value + equation = equation.toLowerCase().replaceAll("p(urse)?", String.valueOf(Utils.getPurse())); + return evaluate(shunt(lex(equation))); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index 086686a7..1aa77080 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -1,6 +1,5 @@ package de.hysky.skyblocker.utils; -import com.google.gson.Gson; import com.google.gson.JsonParser; import com.mojang.authlib.properties.Property; import com.mojang.authlib.properties.PropertyMap; @@ -9,6 +8,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.hysky.skyblocker.SkyblockerMod; import it.unimi.dsi.fastutil.ints.IntIntPair; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.component.ComponentChanges; @@ -49,7 +49,6 @@ public class ItemUtils { private static final DateTimeFormatter OLD_OBTAINED_DATE_FORMAT = DateTimeFormatter.ofPattern("M/d/yy h:m a").withZone(ZoneId.of("UTC")).localizedBy(Locale.ENGLISH); public static final Pattern NOT_DURABILITY = Pattern.compile("[^0-9 /]"); public static final Predicate<String> FUEL_PREDICATE = line -> line.contains("Fuel: "); - private static final Gson GSON = new Gson(); //GSON Instance with no config private static final Codec<RegistryEntry<Item>> EMPTY_ALLOWING_ITEM_CODEC = Registries.ITEM.getEntryCodec(); public static final Codec<ItemStack> EMPTY_ALLOWING_ITEMSTACK_CODEC = Codec.lazyInitialized(() -> RecordCodecBuilder.create(instance -> instance.group( EMPTY_ALLOWING_ITEM_CODEC.fieldOf("id").forGetter(ItemStack::getRegistryEntry), @@ -59,7 +58,7 @@ public class ItemUtils { public static LiteralArgumentBuilder<FabricClientCommandSource> dumpHeldItemCommand() { return literal("dumpHeldItem").executes(context -> { - context.getSource().sendFeedback(Text.literal("[Skyblocker Debug] Held Item: " + GSON.toJson(ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, context.getSource().getPlayer().getMainHandStack()).getOrThrow()))); + context.getSource().sendFeedback(Text.literal("[Skyblocker Debug] Held Item: " + SkyblockerMod.GSON_COMPACT.toJson(ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, context.getSource().getPlayer().getMainHandStack()).getOrThrow()))); return Command.SINGLE_SUCCESS; }); } diff --git a/src/main/java/de/hysky/skyblocker/utils/Location.java b/src/main/java/de/hysky/skyblocker/utils/Location.java index d5214afd..1f6c93a0 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Location.java +++ b/src/main/java/de/hysky/skyblocker/utils/Location.java @@ -87,6 +87,10 @@ public enum Location { */ GLACITE_MINESHAFT("mineshaft"), /** + * Goodbye 1.8 hello 1.21 (and foraging 50 for all)! + */ + MODERN_FORAGING_ISLAND("placeholder"), + /** * Unknown Skyblock location */ UNKNOWN("unknown"); diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 70dbdac6..62a3b897 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -74,6 +74,8 @@ public class Utils { private static long clientWorldJoinTime = 0; private static boolean sentLocRaw = false; private static boolean canSendLocRaw = false; + //This is required to prevent the location change event from being fired twice. + private static boolean locationChanged = true; private static String mayor = ""; @@ -118,6 +120,10 @@ public class Utils { return location == Location.KUUDRAS_HOLLOW; } + public static boolean isInModernForagingIsland() { + return location == Location.MODERN_FORAGING_ISLAND; + } + public static boolean isInjected() { return isInjected; } @@ -379,6 +385,7 @@ public class Utils { MessageScheduler.INSTANCE.sendMessageAfterCooldown("/locraw"); sentLocRaw = true; canSendLocRaw = false; + locationChanged = true; } } else { resetLocRawInfo(); @@ -409,6 +416,11 @@ public class Utils { if (locRaw.has("map")) { map = locRaw.get("map").getAsString(); } + + if (locationChanged) { + SkyblockEvents.LOCATION_CHANGE.invoker().onSkyblockLocationChange(location); + locationChanged = false; + } } /** diff --git a/src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java b/src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java new file mode 100644 index 00000000..f7646d31 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/datafixer/JsonHelper.java @@ -0,0 +1,107 @@ +package de.hysky.skyblocker.utils.datafixer; + +import java.util.Optional; +import java.util.OptionalInt; + +import com.google.gson.JsonObject; + +import net.minecraft.util.annotation.MethodsReturnNonnullByDefault; + +/** + * Helper methods to assist in retrieving values nested in JSON objects. + * + * All methods are fully null safe, whether it be from passing a {@code null} root object or from encountering a nonexistent or null object/value. + * + * @author AzureAaron + * @see <a href="https://github.com/AzureAaron/aaron-mod/blob/1.20/src/main/java/net/azureaaron/mod/utils/JsonHelper.java">Aaron's Mod's JSON Helper</a> + */ +@MethodsReturnNonnullByDefault +public class JsonHelper { + + public static OptionalInt getInt(JsonObject root, String path) { + //If root is null + if (root == null) return OptionalInt.empty(); + + //Fast path for if we just want the field itself + if (!path.contains(".")) { + return root.has(path) && !root.get(path).isJsonNull() ? OptionalInt.of(root.get(path).getAsInt()) : OptionalInt.empty(); + } + + String[] split = path.split("\\."); + String propertyName = split[split.length - 1]; + String[] objects2Traverse = new String[split.length - 1]; + + //Get the traversal path + System.arraycopy(split, 0, objects2Traverse, 0, split.length - 1); + + JsonObject currentLevel = root; + + for (String objectName : objects2Traverse) { + if (currentLevel.has(objectName) && !currentLevel.get(objectName).isJsonNull()) { + currentLevel = currentLevel.getAsJsonObject(objectName); + } else { + return OptionalInt.empty(); + } + } + + return currentLevel.has(propertyName) && !currentLevel.get(propertyName).isJsonNull() ? OptionalInt.of(currentLevel.get(propertyName).getAsInt()) : OptionalInt.empty(); + } + + public static Optional<Boolean> getBoolean(JsonObject root, String path) { + //If root is null + if (root == null) return Optional.empty(); + + //Fast path for if we just want the field itself + if (!path.contains(".")) { + return root.has(path) && !root.get(path).isJsonNull() ? Optional.of(root.get(path).getAsBoolean()) : Optional.empty(); + } + + String[] split = path.split("\\."); + String propertyName = split[split.length - 1]; + String[] objects2Traverse = new String[split.length - 1]; + + //Get the traversal path + System.arraycopy(split, 0, objects2Traverse, 0, split.length - 1); + + JsonObject currentLevel = root; + + for (String objectName : objects2Traverse) { + if (currentLevel.has(objectName) && !currentLevel.get(objectName).isJsonNull()) { + currentLevel = currentLevel.getAsJsonObject(objectName); + } else { + return Optional.empty(); + } + } + + return currentLevel.has(propertyName) && !currentLevel.get(propertyName).isJsonNull() ? Optional.of(currentLevel.get(propertyName).getAsBoolean()) : Optional.empty(); + } + + public static Optional<String> getString(JsonObject root, String path) { + //If root is null + if (root == null) return Optional.empty(); + + //Fast path for if we just want the field itself + if (!path.contains(".")) { + return root.has(path) && !root.get(path).isJsonNull() ? Optional.of(root.get(path).getAsString()) : Optional.empty(); + } + + String[] split = path.split("\\."); + String propertyName = split[split.length - 1]; + String[] objects2Traverse = new String[split.length - 1]; + + //Get the traversal path + System.arraycopy(split, 0, objects2Traverse, 0, split.length - 1); + + JsonObject currentLevel = root; + + for (String objectName : objects2Traverse) { + if (currentLevel.has(objectName) && !currentLevel.get(objectName).isJsonNull()) { + currentLevel = currentLevel.getAsJsonObject(objectName); + } else { + return Optional.empty(); + } + } + + return currentLevel.has(propertyName) && !currentLevel.get(propertyName).isJsonNull() ? Optional.of(currentLevel.get(propertyName).getAsString()) : Optional.empty(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java index e2e057b3..0417dc3c 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java @@ -20,7 +20,7 @@ public abstract class ContainerSolver { protected abstract boolean isEnabled(); - public Pattern getName() { + public final Pattern getName() { return containerName; } @@ -34,12 +34,13 @@ public abstract class ContainerSolver { SkyblockerMod.getInstance().containerSolverManager.markDirty(); } - protected void onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { + protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) { + return false; } protected abstract List<ColorHighlight> getColors(String[] groups, Int2ObjectMap<ItemStack> slots); - protected void trimEdges(Int2ObjectMap<ItemStack> slots, int rows) { + protected final void trimEdges(Int2ObjectMap<ItemStack> slots, int rows) { for (int i = 0; i < rows; i++) { slots.remove(9 * i); slots.remove(9 * i + 8); diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java index b37c57a4..08fb6a86 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java @@ -8,6 +8,7 @@ import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakesHelper; import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit; import de.hysky.skyblocker.skyblock.dungeon.terminal.ColorTerminal; +import de.hysky.skyblocker.skyblock.dungeon.terminal.LightsOnTerminal; import de.hysky.skyblocker.skyblock.dungeon.terminal.OrderTerminal; import de.hysky.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal; import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; @@ -47,6 +48,7 @@ public class ContainerSolverManager { new ColorTerminal(), new OrderTerminal(), new StartsWithTerminal(), + new LightsOnTerminal(), new CroesusHelper(), new CroesusProfit(), new ChronomatronSolver(), @@ -114,10 +116,15 @@ public class ContainerSolverManager { highlights = null; } - public void onSlotClick(int slot, ItemStack stack) { + /** + * @return Whether the click should be disallowed. + */ + public boolean onSlotClick(int slot, ItemStack stack) { if (currentSolver != null) { - currentSolver.onClickSlot(slot, stack, screenId, groups); + return currentSolver.onClickSlot(slot, stack, screenId, groups); } + + return false; } public void onDraw(DrawContext context, List<Slot> slots) { diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 283175d0..4304ff3d 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -89,10 +89,11 @@ "skyblocker.config.dungeons.dungeonScore.enableScoreHUD.deathMessagesNote": "\n\n\nNote: This only works correctly if death messages are enabled in your skyblock settings. If you want to hide death messages, use this mod's Hide Player Death Messages setting instead to allow further processing of death messages.", "skyblocker.config.dungeons.dungeonScore.scoreScaling": "Score Scaling", - "skyblocker.config.dungeons.map.enableMap": "Enable Map", - "skyblocker.config.dungeons.fancyPartyFinder": "Fancy Party Finder", + "skyblocker.config.dungeons.hideSoulweaverSkulls": "Hide Soulweaver Skulls", + "skyblocker.config.dungeons.hideSoulweaverSkulls.@Tooltip": "Hides the Haunted Skulls spawned that are spawned as a result of using Soulweaver Gloves.", + "skyblocker.config.dungeons.livid": "Livid (F5/M5)", "skyblocker.config.dungeons.livid.enableLividColorGlow": "Enable Livid Color Glow", "skyblocker.config.dungeons.livid.enableLividColorGlow.@Tooltip": "Applies the glowing effect to the correct Livid in F5/M5.", @@ -104,6 +105,7 @@ "skyblocker.config.dungeons.livid.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.", "skyblocker.config.dungeons.map": "Map", + "skyblocker.config.dungeons.map.enableMap": "Enable Map", "skyblocker.config.dungeons.map.mapScaling": "Map Scaling", "skyblocker.config.dungeons.map.mapScreen": "Dungeon Map & Score Placement Config...", @@ -168,6 +170,7 @@ "skyblocker.config.dungeons.starredMobGlow.@Tooltip": "Applies the glowing effect to starred mobs that are visible.\n\nWARNING: This feature is experimental and you may encounter issues with it.", "skyblocker.config.dungeons.terminals": "Terminal Solvers (F7/M7)", + "skyblocker.config.dungeons.terminals.blockIncorrectClicks": "Block Incorrect Clicks", "skyblocker.config.dungeons.terminals.solveColor": "Solve Select Colored", "skyblocker.config.dungeons.terminals.solveOrder": "Solve Click In Order", "skyblocker.config.dungeons.terminals.solveStartsWith": "Solve Starts With", @@ -182,6 +185,9 @@ "skyblocker.config.farming.garden.lockMouseTool": "Lock camera when holding a farming tool", "skyblocker.config.farming.garden.visitorHelper": "Visitor helper", + "skyblocker.config.foraging": "Foraging", + "skyblocker.config.foraging.hunting": "Hunting", + "skyblocker.config.general": "General", "skyblocker.config.general.chestValue.@Tooltip": "Calculate the value of this container.", @@ -377,6 +383,22 @@ "skyblocker.config.mining": "Mining", + "skyblocker.config.mining.commissionWaypoints": "Commission Waypoints", + "skyblocker.config.mining.commissionWaypoints.mode": "Enable Commission Waypoints", + "skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[0]": "Off: Do not show Commission waypoint.", + "skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[1]": "\nDwarven: Show waypoints only in dwarven mines.", + "skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[2]": "\nGlacite: Show waypoints only in glacite tunnles.", + "skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[3]": "\nBoth: Show waypoints in dwarven mines and glacite tunnels.", + "skyblocker.config.mining.commissionWaypoints.mode.@Tooltip[4]": "\n(takes effect when commissions updated)", + "skyblocker.config.mining.commissionWaypoints.useColor": "Colored Waypoints", + "skyblocker.config.mining.commissionWaypoints.useColor.@Tooltip": "Color the waypoint text to match the block it's for.", + "skyblocker.config.mining.commissionWaypoints.textScale": "Text Scale", + "skyblocker.config.mining.commissionWaypoints.textScale.@Tooltip": "Scale the size of the commission labels.", + "skyblocker.config.mining.commissionWaypoints.showBaseCamp": "Show Basecamp Waypoint", + "skyblocker.config.mining.commissionWaypoints.showBaseCamp.@Tooltip": "Show waypoint for basecamp when in glacite tunnels (takes effect when commissions updated).", + "skyblocker.config.mining.commissionWaypoints.showEmissary": "Show Emissary", + "skyblocker.config.mining.commissionWaypoints.showEmissary.@Tooltip": "When a commission in the dwarven mines is finished show emissary locations (takes effect when commissions updated).", + "skyblocker.config.mining.crystalHollows": "Crystal Hollows", "skyblocker.config.mining.crystalHollows.metalDetectorHelper": "Metal Detector Helper", "skyblocker.config.mining.crystalHollows.metalDetectorHelper.@Tooltip": "Helper for the metal detector puzzle in the Mines of Divan.", @@ -454,14 +476,15 @@ "skyblocker.config.quickNav": "Quick Navigation", "skyblocker.config.quickNav.button": "Button %d", - "skyblocker.config.quickNav.button.clickEvent": "Click event", + "skyblocker.config.quickNav.button.clickEvent": "Click Event", + "skyblocker.config.quickNav.button.item.components": "Item Components", + "skyblocker.config.quickNav.button.item.components.@Tooltip": "A string of item components enclosed in square brackets, which is the same format as the /give command.\n\nExample: [minecraft:enchantment_glint_override=true]", "skyblocker.config.quickNav.button.item.count": "Item Count", - "skyblocker.config.quickNav.button.item.itemName": "Item ID", - "skyblocker.config.quickNav.button.item.nbt": "NBT", - "skyblocker.config.quickNav.button.item.nbt.@Tooltip": "\u00a7c\u00a7lWARNING: THIS MUST BE CONFIGURED WITH ITEM NBT NOT ITEM COMPONENTS! \n\n\u00a7fIn the future we will automatically migrate your config to use item components!", + "skyblocker.config.quickNav.button.item.itemName": "Item", "skyblocker.config.quickNav.button.render": "Render", "skyblocker.config.quickNav.button.uiTitle": "UI Title", "skyblocker.config.quickNav.enableQuickNav": "Enable Quick Navigation", + "skyblocker.config.quickNav.enableExtendedQuickNav": "Enable Extended Quick Navigation", "skyblocker.config.slayer": "Slayers", @@ -504,6 +527,14 @@ "skyblocker.config.uiAndVisuals.chestValue.incompleteColor": "Incomplete Color", "skyblocker.config.uiAndVisuals.chestValue.incompleteColor.@Tooltip": "The color to display when the price data is incomplete.", + "skyblocker.config.uiAndVisuals.compactDamage": "Compact Damage", + "skyblocker.config.uiAndVisuals.compactDamage.enabled": "Enabled", + "skyblocker.config.uiAndVisuals.compactDamage.precision": "Precision", + "skyblocker.config.uiAndVisuals.compactDamage.precision.@Tooltip" : "The number of digits to display after the decimal point.", + "skyblocker.config.uiAndVisuals.compactDamage.normalDamageColor": "Normal Damage Color", + "skyblocker.config.uiAndVisuals.compactDamage.critDamageGradientStart": "Crit Damage Gradient Start Color", + "skyblocker.config.uiAndVisuals.compactDamage.critDamageGradientEnd": "Crit Damage Gradient End Color", + "skyblocker.config.uiAndVisuals.compactorDeletorPreview": "Enable Compactor/Deletor Preview", "skyblocker.config.uiAndVisuals.dontStripSkinAlphaValues": "Correct Transparent Skin Pixels", @@ -520,6 +551,13 @@ "skyblocker.config.uiAndVisuals.flameOverlay.flameOpacity": "Flame Opacity", "skyblocker.config.uiAndVisuals.flameOverlay.flameOpacity.@Tooltip": "100% default opacity\n0% off", + "skyblocker.config.uiAndVisuals.inputCalculator": "Input Calculator", + "skyblocker.config.uiAndVisuals.inputCalculator.enabled": "Enable Sign Calculator", + "skyblocker.config.uiAndVisuals.inputCalculator.enabled.@Tooltip": "Enables the ability for you to do calculations when inputting values such as price for the ah.\n Key:\n S = 64\n E = 160\n K = 1,000\n M = 1,000,000\n B = 1,000,000,000\n\n purse/P = current purse value", + "skyblocker.config.uiAndVisuals.inputCalculator.requiresEquals": "Only show with \"=\".", + "skyblocker.config.uiAndVisuals.inputCalculator.requiresEquals.@Tooltip": "Only show the calculator when the message start with \"=\".", + "skyblocker.config.uiAndVisuals.inputCalculator.invalidEquation": "Invalid Equation", + "skyblocker.config.uiAndVisuals.itemCooldown": "Item Cooldown", "skyblocker.config.uiAndVisuals.itemCooldown.enableItemCooldowns": "Enable Item Cooldown", diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 0032a557..828cc206 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -32,6 +32,7 @@ "PlayerSkinTextureMixin", "RenderFishMixin", "ScoreboardMixin", + "SignEditScreenMixin", "SocialInteractionsPlayerListWidgetMixin", "WindowMixin", "WorldRendererMixin", diff --git a/src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java b/src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java new file mode 100644 index 00000000..fcdbc314 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/config/datafixer/ConfigDataFixerTest.java @@ -0,0 +1,42 @@ +package de.hysky.skyblocker.config.datafixer; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import net.minecraft.Bootstrap; +import net.minecraft.SharedConstants; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.InputStreamReader; + +public class ConfigDataFixerTest { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + @BeforeAll + public static void setupEnvironment() { + SharedConstants.createGameVersion(); + Bootstrap.initialize(); + } + + @Test + void testDataFixer1() { + @SuppressWarnings("DataFlowIssue") + JsonObject oldConfig = GSON.fromJson(new InputStreamReader(ConfigDataFixerTest.class.getResourceAsStream("/assets/skyblocker/config/skyblocker-v1.json")), JsonObject.class); + @SuppressWarnings("DataFlowIssue") + JsonObject expectedNewConfig = GSON.fromJson(new InputStreamReader(ConfigDataFixerTest.class.getResourceAsStream("/assets/skyblocker/config/skyblocker-v2.json")), JsonObject.class); + + Assertions.assertEquals(expectedNewConfig, ConfigDataFixer.apply(oldConfig, 2)); + } + + @Test + void testDataFixer2QuickNav() { + @SuppressWarnings("DataFlowIssue") + JsonObject oldConfig = GSON.fromJson(new InputStreamReader(ConfigDataFixerTest.class.getResourceAsStream("/assets/skyblocker/config/skyblocker-v2.json")), JsonObject.class); + @SuppressWarnings("DataFlowIssue") + JsonObject expectedNewConfig = GSON.fromJson(new InputStreamReader(ConfigDataFixerTest.class.getResourceAsStream("/assets/skyblocker/config/skyblocker-v3.json")), JsonObject.class); + + Assertions.assertEquals(expectedNewConfig, ConfigDataFixer.apply(oldConfig)); + } +} diff --git a/src/test/java/de/hysky/skyblocker/utils/CalculatorTest.java b/src/test/java/de/hysky/skyblocker/utils/CalculatorTest.java new file mode 100644 index 00000000..c29efdf2 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/utils/CalculatorTest.java @@ -0,0 +1,31 @@ +package de.hysky.skyblocker.utils; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class CalculatorTest { + @Test + void testShorthands() { + Assertions.assertEquals(Calculator.calculate("1k"), 1000); + Assertions.assertEquals(Calculator.calculate("0.12k"), 120); + Assertions.assertEquals(Calculator.calculate("1k + 0.12k"), 1120); + Assertions.assertEquals(Calculator.calculate("1 + 1s + 1k + 1m + 1b"), 1001001065); + } + + @Test + void testPrecedence() { + Assertions.assertEquals(Calculator.calculate("5 + 2 * 2"), 9); + Assertions.assertEquals(Calculator.calculate("5 - 2 / 2"), 4); + Assertions.assertEquals(Calculator.calculate("5 * (1 + 2)"), 15); + } + + @Test + void testImplicitMultiplication() { + Assertions.assertEquals(Calculator.calculate("5(2 + 2)"), 20); + } + + @Test + void testImplicitClosingParenthesis() { + Assertions.assertEquals(Calculator.calculate("5(2 + 2"), 20); + } +} diff --git a/src/test/resources/assets/skyblocker/config/skyblocker-v1.json b/src/test/resources/assets/skyblocker/config/skyblocker-v1.json new file mode 100644 index 00000000..20620a57 --- /dev/null +++ b/src/test/resources/assets/skyblocker/config/skyblocker-v1.json @@ -0,0 +1,555 @@ +{ + "version": 1, + "general": { + "enableTips": true, + "acceptReparty": true, + "betterPartyFinder": true, + "fancyCraftingTable": true, + "backpackPreviewWithoutShift": true, + "compactorDeletorPreview": true, + "hideEmptyTooltips": true, + "hideStatusEffectOverlay": false, + "dontStripSkinAlphaValues": true, + "dungeonQuality": true, + "enableNewYearCakesHelper": true, + "tabHud": { + "tabHudEnabled": true, + "tabHudScale": 100, + "enableHudBackground": true, + "plainPlayerNames": false, + "nameSorting": "ALPHABETICAL" + }, + "bars": { + "enableBars": true, + "barPositions": { + "healthBarPosition": "LAYER1", + "manaBarPosition": "LAYER1", + "defenceBarPosition": "RIGHT", + "experienceBarPosition": "RIGHT" + } + }, + "experiments": { + "enableChronomatronSolver": true, + "enableSuperpairsSolver": true, + "enableUltrasequencerSolver": true + }, + "fishing": { + "enableFishingHelper": true, + "enableFishingTimer": false, + "changeTimerColor": true, + "fishingTimerScale": 1.0, + "hideOtherPlayersRods": false + }, + "fairySouls": { + "enableFairySoulsHelper": true, + "highlightFoundSouls": false, + "highlightOnlyNearbySouls": false + }, + "mythologicalRitual": { + "enableMythologicalRitualHelper": true + }, + "itemCooldown": { + "enableItemCooldowns": true + }, + "shortcuts": { + "enableShortcuts": true, + "enableCommandShortcuts": true, + "enableCommandArgShortcuts": true + }, + "waypoints": { + "enableWaypoints": true, + "waypointType": "WAYPOINT" + }, + "quiverWarning": { + "enableQuiverWarning": true, + "enableQuiverWarningInDungeons": true, + "enableQuiverWarningAfterDungeon": true + }, + "itemList": { + "enableItemList": true + }, + "itemTooltip": { + "enableNPCPrice": true, + "enableMotesPrice": true, + "enableAvgBIN": true, + "avg": "THREE_DAY", + "enableLowestBIN": true, + "enableBazaarPrice": true, + "enableObtainedDate": true, + "enableMuseumInfo": true, + "enableExoticTooltip": true, + "enableAccessoriesHelper": true + }, + "itemInfoDisplay": { + "attributeShardInfo": true, + "itemRarityBackgrounds": true, + "itemRarityBackgroundStyle": "CIRCULAR", + "itemRarityBackgroundsOpacity": 0.5 + }, + "itemProtection": { + "slotLockStyle": "FANCY" + }, + "wikiLookup": { + "enableWikiLookup": true, + "officialWiki": false + }, + "chestValue": { + "enableChestValue": true, + "color": "DARK_GREEN", + "incompleteColor": "BLUE" + }, + "specialEffects": { + "rareDungeonDropEffects": true + }, + "hitbox": { + "oldFarmlandHitbox": true, + "oldLeverHitbox": false + }, + "titleContainer": { + "titleContainerScale": 100.0, + "x": 482, + "y": 170, + "direction": "VERTICAL", + "alignment": "MIDDLE" + }, + "teleportOverlay": { + "enableTeleportOverlays": true, + "enableWeirdTransmission": true, + "enableInstantTransmission": true, + "enableEtherTransmission": true, + "enableSinrecallTransmission": true, + "enableWitherImpact": true + }, + "flameOverlay": { + "flameHeight": 0, + "flameOpacity": 0 + }, + "searchOverlay": { + "enableBazaar": true, + "enableAuctionHouse": true, + "keepPreviousSearches": false, + "maxSuggestions": 5, + "historyLength": 5, + "enableCommands": false, + "bazaarHistory": [ + "Enchanted Snow Block", + "Recombobulator 3000", + "Enchanted Snow Block", + "Recombobulator 3000", + "Enchanted Snow Block" + ], + "auctionHistory": [ + "God Potion", + "New Year Cake (Year 30", + "New Year Cake (Year 31", + "New Year Cake (Year 32", + "New Year Cake (Year 2" + ] + }, + "fancyAuctionHouse": { + "enabled": true, + "highlightCheapBIN": true + }, + "lockedSlots": [ + 3, + 8, + 0 + ], + "protectedItems": [ + "10f84455-c063-4e3e-b6eb-cc485fe74a06", + "64e491fa-64ed-4abf-829a-940e1fb295d2", + "23a619b6-f487-419e-8598-10b8f21dc80a", + "416aa42d-27d4-4284-913f-d03fa1f528c3", + "4e2a7f16-7d93-4069-bfbd-a8ff8e42f285", + "63ec4f84-2005-45e5-a4b7-0c9ae84dc612", + "aba2b017-91e8-4263-ae9e-5a10f8dc91fe", + "f1391873-5126-496d-b0fe-c861faeaf681", + "bb84cfea-3fdd-4579-b5fa-907cd8123593", + "a604f533-49f6-4190-b971-08f725231229", + "333cba8d-3136-4494-9fa0-40dff482ef66", + "91f49338-fc0b-4601-a704-aabc44853318", + "d76e5eca-be47-4d0b-973c-3ec30fd176dc", + "301c58a7-9b8a-4c4e-9e8d-3e8a9471a916", + "c8ab629e-fc5b-4af0-8053-acb155411c9b", + "333a079f-93d4-48f1-af9f-862b1ecc417c", + "0843d8b0-b6a6-47fc-afde-8eee8c5455d9", + "8bcf2a99-68e6-4646-ac04-4c72e55c6818", + "214333db-d71c-4080-8487-00b4666bb9a4", + "1eba38b5-e0f6-4862-bf19-e2df33e21ce4", + "6b42c77f-1891-43a4-a9ea-4454e299a059", + "71ce1812-d1fd-4e71-98ad-8b863759a82f", + "253489d9-49d0-46c8-b3f8-b70c3663c5d2", + "6033eff2-879d-494f-8dae-696fc8a3c82e", + "5f6090c8-cbc4-4030-97ac-5018c151cc15", + "64162e02-91fb-4ec3-a57c-866436a99a7a", + "a299c718-c66b-4de5-9747-71c172ac4601", + "e21172ce-0a41-426a-a445-84a28677cd65", + "4bfd23bf-ceb8-4951-874d-e61633e088d8", + "575ce2c9-251e-4435-9020-de1a2e24b1d0", + "b06b8fe2-470a-43f3-b844-658472f20996", + "e54dea3d-ca45-451e-9ae4-0e5fb87e97b2", + "67b47348-da46-4257-833a-dd23fb074cc6", + "9f7597ec-c3c9-46df-9f36-8e76c8745a9d", + "e297dcef-2f3d-4fb3-9781-aaff6017178c", + "ad9a598d-e5c9-4293-92c8-5e41bebe913c", + "fa51c660-b262-4826-bed8-6138236919cb" + ], + "customItemNames": {}, + "customDyeColors": {}, + "customArmorTrims": { + "f1391873-5126-496d-b0fe-c861faeaf681": { + "material": "minecraft:amethyst", + "pattern": "minecraft:raiser" + }, + "81409da1-610c-445a-aba2-4c95a3cabbf2": { + "material": "minecraft:amethyst", + "pattern": "minecraft:coast" + } + }, + "customAnimatedDyes": { + "f1391873-5126-496d-b0fe-c861faeaf681": { + "color1": 16711680, + "color2": 11154282, + "samples": 10, + "cycleBack": true, + "tickDelay": 4 + } + } + }, + "locations": { + "barn": { + "solveHungryHiker": true, + "solveTreasureHunter": true + }, + "crimsonIsle": { + "kuudra": { + "supplyWaypoints": true, + "fuelWaypoints": true, + "suppliesAndFuelWaypointType": "WAYPOINT", + "ballistaBuildWaypoints": true, + "safeSpotWaypoints": true, + "pearlWaypoints": true, + "noArrowPoisonWarning": true, + "arrowPoisonThreshold": 16 + } + }, + "dungeons": { + "secretWaypoints": { + "enableRoomMatching": true, + "enableSecretWaypoints": true, + "waypointType": "OUTLINED_HIGHLIGHT", + "showSecretText": true, + "enableEntranceWaypoints": true, + "enableSuperboomWaypoints": true, + "enableChestWaypoints": true, + "enableItemWaypoints": true, + "enableBatWaypoints": true, + "enableWitherWaypoints": true, + "enableLeverWaypoints": true, + "enableFairySoulWaypoints": true, + "enableStonkWaypoints": false, + "enableAotvWaypoints": true, + "enablePearlWaypoints": true, + "enableDefaultWaypoints": true + }, + "doorHighlight": { + "enableDoorHighlight": true, + "doorHighlightType": "OUTLINED_HIGHLIGHT" + }, + "dungeonScore": { + "enableDungeonScore270Message": false, + "enableDungeonScore270Title": false, + "enableDungeonScore270Sound": false, + "dungeonScore270Message": "270 Score Reached!", + "enableDungeonScore300Message": true, + "enableDungeonScore300Title": true, + "enableDungeonScore300Sound": true, + "dungeonScore300Message": "[做得好] 300 Social Credit has been added to your account. Enjoy your boss fight.", + "enableDungeonCryptsMessage": true, + "dungeonCryptsMessageThreshold": 250, + "dungeonCryptsMessage": "We only have [crypts] crypts out of 5, we need more!", + "enableScoreHUD": true, + "scoreX": 25, + "scoreY": 134, + "scoreScaling": 1.0 + }, + "dungeonChestProfit": { + "enableProfitCalculator": true, + "includeKismet": true, + "includeEssence": false, + "croesusProfit": true, + "neutralThreshold": 1000, + "neutralColor": "DARK_GRAY", + "profitColor": "DARK_GREEN", + "lossColor": "RED", + "incompleteColor": "BLUE" + }, + "mimicMessage": { + "sendMimicMessage": true, + "mimicMessage": "Mimic dead!" + }, + "croesusHelper": true, + "enableMap": true, + "mapScaling": 1.0, + "mapX": 2, + "mapY": 2, + "playerSecretsTracker": true, + "starredMobGlow": true, + "starredMobBoundingBoxes": true, + "solveThreeWeirdos": true, + "blazeSolver": true, + "creeperSolver": true, + "solveTrivia": true, + "solveTicTacToe": true, + "solveWaterboard": true, + "solveBoulder": true, + "solveIceFill": true, + "solveSilverfish": true, + "fireFreezeStaffTimer": true, + "floor3GuardianHealthDisplay": true, + "allowDroppingProtectedItems": false, + "lividColor": { + "enableLividColorGlow": true, + "enableLividColorText": true, + "enableLividColorTitle": true, + "lividColorText": "[color] is sus" + }, + "terminals": { + "solveColor": true, + "solveOrder": true, + "solveStartsWith": true + } + }, + "dwarvenMines": { + "enableDrillFuel": true, + "solveFetchur": true, + "solvePuzzler": true, + "metalDetectorHelper": true, + "dwarvenHud": { + "enabledCommissions": true, + "enabledPowder": true, + "style": "SIMPLE", + "x": 10, + "y": 10, + "powderX": 10, + "powderY": 70 + }, + "crystalsHud": { + "enabled": true, + "showLocations": true, + "locationSize": 8, + "x": 10, + "y": 130, + "mapScaling": 1.0 + }, + "crystalsWaypoints": { + "enabled": true, + "findInChat": true + } + }, + "rift": { + "mirrorverseWaypoints": true, + "blobbercystGlow": true, + "enigmaSoulWaypoints": false, + "highlightFoundEnigmaSouls": true, + "mcGrubberStacks": 0 + }, + "end": { + "enableEnderNodeHelper": true, + "hudEnabled": true, + "zealotKillsEnabled": true, + "protectorLocationEnabled": true, + "waypoint": true, + "x": 10, + "y": 10 + }, + "spidersDen": { + "relics": { + "enableRelicsHelper": true, + "highlightFoundRelics": false + } + }, + "garden": { + "farmingHud": { + "enableHud": true, + "x": 180, + "y": 0 + }, + "dicerTitlePrevent": true, + "visitorHelper": true, + "lockMouseTool": true, + "lockMouseGroundOnly": true + } + }, + "slayer": { + "endermanSlayer": { + "enableYangGlyphsNotification": true, + "highlightBeacons": true, + "highlightNukekubiHeads": true + }, + "vampireSlayer": { + "enableEffigyWaypoints": true, + "compactEffigyWaypoints": false, + "effigyUpdateFrequency": 5, + "enableHolyIceIndicator": true, + "holyIceIndicatorTickDelay": 10, + "holyIceUpdateFrequency": 5, + "enableHealingMelonIndicator": true, + "healingMelonHealthThreshold": 4.0, + "enableSteakStakeIndicator": true, + "steakStakeUpdateFrequency": 5, + "enableManiaIndicator": true, + "maniaUpdateFrequency": 5 + } + }, + "quickNav": { + "enableQuickNav": true, + "button1": { + "render": true, + "item": { + "itemName": "diamond_sword", + "count": 1, + "nbt": "" + }, + "uiTitle": "Your Skills", + "clickEvent": "/skills" + }, + "button2": { + "render": true, + "item": { + "itemName": "painting", + "count": 1, + "nbt": "" + }, + "uiTitle": "Collections", + "clickEvent": "/collection" + }, + "button3": { + "render": true, + "item": { + "itemName": "bone", + "count": 1, + "nbt": "" + }, + "uiTitle": "Pets(:? \\(\\d+\\/\\d+\\))?", + "clickEvent": "/pets" + }, + "button4": { + "render": true, + "item": { + "itemName": "leather_chestplate", + "count": 1, + "nbt": "tag:{display:{color:8991416}}" + }, + "uiTitle": "Wardrobe \\([12]/2\\)", + "clickEvent": "/wardrobe" + }, + "button5": { + "render": true, + "item": { + "itemName": "player_head", + "count": 1, + "nbt": "tag:{SkullOwner:{Id:[I;-2081424676,-57521078,-2073572414,158072763],Properties:{textures:[{Value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0=\"}]}}}" + }, + "uiTitle": "Sack of Sacks", + "clickEvent": "/sacks" + }, + "button6": { + "render": true, + "item": { + "itemName": "ender_chest", + "count": 1, + "nbt": "" + }, + "uiTitle": "Storage", + "clickEvent": "/storage" + }, + "button7": { + "render": true, + "item": { + "itemName": "player_head", + "count": 1, + "nbt": "tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}" + }, + "uiTitle": "none", + "clickEvent": "/hub" + }, + "button8": { + "render": true, + "item": { + "itemName": "player_head", + "count": 1, + "nbt": "tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}" + }, + "uiTitle": "none", + "clickEvent": "/warp dungeon_hub" + }, + "button9": { + "render": true, + "item": { + "itemName": "player_head", + "count": 1, + "nbt": "tag:{SkullOwner:{Id:[I;-562285948,532499670,-1705302742,775653035],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0=\"}]}}}" + }, + "uiTitle": "Visit prtl", + "clickEvent": "/visit prtl" + }, + "button10": { + "render": true, + "item": { + "itemName": "enchanting_table", + "count": 1, + "nbt": "" + }, + "uiTitle": "Enchant Item", + "clickEvent": "/etable" + }, + "button11": { + "render": true, + "item": { + "itemName": "anvil", + "count": 1, + "nbt": "" + }, + "uiTitle": "Anvil", + "clickEvent": "/anvil" + }, + "button12": { + "render": true, + "item": { + "itemName": "crafting_table", + "count": 1, + "nbt": "" + }, + "uiTitle": "Craft Item", + "clickEvent": "/craft" + } + }, + "messages": { + "hideAbility": "ACTION_BAR", + "hideHeal": "PASS", + "hideAOTE": "ACTION_BAR", + "hideImplosion": "PASS", + "hideMoltenWave": "PASS", + "hideAds": "FILTER", + "hideTeleportPad": "PASS", + "hideCombo": "PASS", + "hideAutopet": "PASS", + "hideShowOff": "FILTER", + "hideToggleSkyMall": "PASS", + "hideMimicKill": "PASS", + "hideDeath": "PASS", + "hideMana": false, + "hideDicer": "PASS", + "chatRuleConfig": { + "announcementLength": 60, + "announcementScale": 3 + } + }, + "richPresence": { + "enableRichPresence": false, + "info": "LOCATION", + "cycleMode": false, + "customMessage": "Playing Skyblock" + } +}
\ No newline at end of file diff --git a/src/test/resources/assets/skyblocker/config/skyblocker-v2.json b/src/test/resources/assets/skyblocker/config/skyblocker-v2.json new file mode 100644 index 00000000..e8489a5d --- /dev/null +++ b/src/test/resources/assets/skyblocker/config/skyblocker-v2.json @@ -0,0 +1,573 @@ +{ + "version": 2, + "general": { + "enableTips": true, + "acceptReparty": true, + "shortcuts": { + "enableShortcuts": true, + "enableCommandShortcuts": true, + "enableCommandArgShortcuts": true + }, + "quiverWarning": { + "enableQuiverWarning": true, + "enableQuiverWarningInDungeons": true, + "enableQuiverWarningAfterDungeon": true + }, + "itemList": { + "enableItemList": true + }, + "itemTooltip": { + "enableNPCPrice": true, + "enableMotesPrice": true, + "enableAvgBIN": true, + "avg": "THREE_DAY", + "enableLowestBIN": true, + "enableBazaarPrice": true, + "enableObtainedDate": true, + "enableMuseumInfo": true, + "enableExoticTooltip": true, + "enableAccessoriesHelper": true, + "dungeonQuality": true + }, + "itemInfoDisplay": { + "attributeShardInfo": true, + "itemRarityBackgrounds": true, + "itemRarityBackgroundStyle": "CIRCULAR", + "itemRarityBackgroundsOpacity": 0.5 + }, + "itemProtection": { + "slotLockStyle": "FANCY" + }, + "wikiLookup": { + "enableWikiLookup": true, + "officialWiki": false + }, + "specialEffects": { + "rareDungeonDropEffects": true + }, + "hitbox": { + "oldFarmlandHitbox": true, + "oldLeverHitbox": false + }, + "lockedSlots": [ + 3, + 8, + 0 + ], + "protectedItems": [ + "10f84455-c063-4e3e-b6eb-cc485fe74a06", + "64e491fa-64ed-4abf-829a-940e1fb295d2", + "23a619b6-f487-419e-8598-10b8f21dc80a", + "416aa42d-27d4-4284-913f-d03fa1f528c3", + "4e2a7f16-7d93-4069-bfbd-a8ff8e42f285", + "63ec4f84-2005-45e5-a4b7-0c9ae84dc612", + "aba2b017-91e8-4263-ae9e-5a10f8dc91fe", + "f1391873-5126-496d-b0fe-c861faeaf681", + "bb84cfea-3fdd-4579-b5fa-907cd8123593", + "a604f533-49f6-4190-b971-08f725231229", + "333cba8d-3136-4494-9fa0-40dff482ef66", + "91f49338-fc0b-4601-a704-aabc44853318", + "d76e5eca-be47-4d0b-973c-3ec30fd176dc", + "301c58a7-9b8a-4c4e-9e8d-3e8a9471a916", + "c8ab629e-fc5b-4af0-8053-acb155411c9b", + "333a079f-93d4-48f1-af9f-862b1ecc417c", + "0843d8b0-b6a6-47fc-afde-8eee8c5455d9", + "8bcf2a99-68e6-4646-ac04-4c72e55c6818", + "214333db-d71c-4080-8487-00b4666bb9a4", + "1eba38b5-e0f6-4862-bf19-e2df33e21ce4", + "6b42c77f-1891-43a4-a9ea-4454e299a059", + "71ce1812-d1fd-4e71-98ad-8b863759a82f", + "253489d9-49d0-46c8-b3f8-b70c3663c5d2", + "6033eff2-879d-494f-8dae-696fc8a3c82e", + "5f6090c8-cbc4-4030-97ac-5018c151cc15", + "64162e02-91fb-4ec3-a57c-866436a99a7a", + "a299c718-c66b-4de5-9747-71c172ac4601", + "e21172ce-0a41-426a-a445-84a28677cd65", + "4bfd23bf-ceb8-4951-874d-e61633e088d8", + "575ce2c9-251e-4435-9020-de1a2e24b1d0", + "b06b8fe2-470a-43f3-b844-658472f20996", + "e54dea3d-ca45-451e-9ae4-0e5fb87e97b2", + "67b47348-da46-4257-833a-dd23fb074cc6", + "9f7597ec-c3c9-46df-9f36-8e76c8745a9d", + "e297dcef-2f3d-4fb3-9781-aaff6017178c", + "ad9a598d-e5c9-4293-92c8-5e41bebe913c", + "fa51c660-b262-4826-bed8-6138236919cb" + ], + "customItemNames": {}, + "customDyeColors": {}, + "customArmorTrims": { + "f1391873-5126-496d-b0fe-c861faeaf681": { + "material": "minecraft:amethyst", + "pattern": "minecraft:raiser" + }, + "81409da1-610c-445a-aba2-4c95a3cabbf2": { + "material": "minecraft:amethyst", + "pattern": "minecraft:coast" + } + }, + "customAnimatedDyes": { + "f1391873-5126-496d-b0fe-c861faeaf681": { + "color1": 16711680, + "color2": 11154282, + "samples": 10, + "cycleBack": true, + "tickDelay": 4 + } + } + }, + "uiAndVisuals": { + "compactorDeletorPreview": true, + "dontStripSkinAlphaValues": true, + "backpackPreviewWithoutShift": true, + "fancyCraftingTable": true, + "hideStatusEffectOverlay": false, + "chestValue": { + "enableChestValue": true, + "color": "DARK_GREEN", + "incompleteColor": "BLUE" + }, + "itemCooldown": { + "enableItemCooldowns": true + }, + "titleContainer": { + "titleContainerScale": 100.0, + "x": 482, + "y": 170, + "direction": "VERTICAL", + "alignment": "MIDDLE" + }, + "tabHud": { + "tabHudEnabled": true, + "tabHudScale": 100, + "enableHudBackground": true, + "plainPlayerNames": false, + "nameSorting": "ALPHABETICAL" + }, + "fancyAuctionHouse": { + "enabled": true, + "highlightCheapBIN": true + }, + "bars": { + "enableBars": true, + "barPositions": { + "healthBarPosition": "LAYER1", + "manaBarPosition": "LAYER1", + "defenceBarPosition": "RIGHT", + "experienceBarPosition": "RIGHT" + } + }, + "waypoints": { + "enableWaypoints": true, + "waypointType": "WAYPOINT" + }, + "teleportOverlay": { + "enableTeleportOverlays": true, + "enableWeirdTransmission": true, + "enableInstantTransmission": true, + "enableEtherTransmission": true, + "enableSinrecallTransmission": true, + "enableWitherImpact": true + }, + "searchOverlay": { + "enableBazaar": true, + "enableAuctionHouse": true, + "keepPreviousSearches": false, + "maxSuggestions": 5, + "historyLength": 5, + "enableCommands": false, + "bazaarHistory": [ + "Enchanted Snow Block", + "Recombobulator 3000", + "Enchanted Snow Block", + "Recombobulator 3000", + "Enchanted Snow Block" + ], + "auctionHistory": [ + "God Potion", + "New Year Cake (Year 30", + "New Year Cake (Year 31", + "New Year Cake (Year 32", + "New Year Cake (Year 2" + ] + }, + "flameOverlay": { + "flameHeight": 0, + "flameOpacity": 0 + }, + "hideEmptyTooltips": true + }, + "helpers": { + "enableNewYearCakesHelper": true, + "mythologicalRitual": { + "enableMythologicalRitualHelper": true + }, + "experiments": { + "enableChronomatronSolver": true, + "enableSuperpairsSolver": true, + "enableUltrasequencerSolver": true + }, + "fishing": { + "enableFishingHelper": true, + "enableFishingTimer": false, + "changeTimerColor": true, + "fishingTimerScale": 1.0, + "hideOtherPlayersRods": false + }, + "fairySouls": { + "enableFairySoulsHelper": true, + "highlightFoundSouls": false, + "highlightOnlyNearbySouls": false + } + }, + "dungeons": { + "fancyPartyFinder": true, + "croesusHelper": true, + "playerSecretsTracker": true, + "starredMobGlow": true, + "starredMobBoundingBoxes": true, + "allowDroppingProtectedItems": false, + "dungeonMap": { + "enableMap": true, + "mapScaling": 1.0, + "mapX": 2, + "mapY": 2 + }, + "puzzleSolvers": { + "solveThreeWeirdos": true, + "blazeSolver": true, + "creeperSolver": true, + "solveTrivia": true, + "solveTicTacToe": true, + "solveWaterboard": true, + "solveBoulder": true, + "solveIceFill": true, + "solveSilverfish": true + }, + "theProfessor": { + "fireFreezeStaffTimer": true, + "floor3GuardianHealthDisplay": true + }, + "livid": { + "enableLividColorGlow": true, + "enableLividColorText": true, + "enableLividColorTitle": true, + "lividColorText": "[color] is sus" + }, + "terminals": { + "solveColor": true, + "solveOrder": true, + "solveStartsWith": true + }, + "secretWaypoints": { + "enableRoomMatching": true, + "enableSecretWaypoints": true, + "waypointType": "OUTLINED_HIGHLIGHT", + "showSecretText": true, + "enableEntranceWaypoints": true, + "enableSuperboomWaypoints": true, + "enableChestWaypoints": true, + "enableItemWaypoints": true, + "enableBatWaypoints": true, + "enableWitherWaypoints": true, + "enableLeverWaypoints": true, + "enableFairySoulWaypoints": true, + "enableStonkWaypoints": false, + "enableAotvWaypoints": true, + "enablePearlWaypoints": true, + "enableDefaultWaypoints": true + }, + "mimicMessage": { + "sendMimicMessage": true, + "mimicMessage": "Mimic dead!" + }, + "doorHighlight": { + "enableDoorHighlight": true, + "doorHighlightType": "OUTLINED_HIGHLIGHT" + }, + "dungeonScore": { + "enableDungeonScore270Message": false, + "enableDungeonScore270Title": false, + "enableDungeonScore270Sound": false, + "dungeonScore270Message": "270 Score Reached!", + "enableDungeonScore300Message": true, + "enableDungeonScore300Title": true, + "enableDungeonScore300Sound": true, + "dungeonScore300Message": "[做得好] 300 Social Credit has been added to your account. Enjoy your boss fight.", + "enableDungeonCryptsMessage": true, + "dungeonCryptsMessageThreshold": 250, + "dungeonCryptsMessage": "We only have [crypts] crypts out of 5, we need more!", + "enableScoreHUD": true, + "scoreX": 25, + "scoreY": 134, + "scoreScaling": 1.0 + }, + "dungeonChestProfit": { + "enableProfitCalculator": true, + "includeKismet": true, + "includeEssence": false, + "croesusProfit": true, + "neutralThreshold": 1000, + "neutralColor": "DARK_GRAY", + "profitColor": "DARK_GREEN", + "lossColor": "RED", + "incompleteColor": "BLUE" + } + }, + "crimsonIsle": { + "kuudra": { + "supplyWaypoints": true, + "fuelWaypoints": true, + "suppliesAndFuelWaypointType": "WAYPOINT", + "ballistaBuildWaypoints": true, + "safeSpotWaypoints": true, + "pearlWaypoints": true, + "noArrowPoisonWarning": true, + "arrowPoisonThreshold": 16 + } + }, + "mining": { + "enableDrillFuel": true, + "dwarvenMines": { + "solveFetchur": true, + "solvePuzzler": true + }, + "dwarvenHud": { + "enabledCommissions": true, + "enabledPowder": true, + "style": "SIMPLE", + "commissionsX": 10, + "commissionsY": 10, + "powderX": 10, + "powderY": 70 + }, + "crystalsHud": { + "enabled": true, + "showLocations": true, + "locationSize": 8, + "x": 10, + "y": 130, + "mapScaling": 1.0 + }, + "crystalsWaypoints": { + "enabled": true, + "findInChat": true + }, + "crystalHollows": { + "metalDetectorHelper": true + } + }, + "farming": { + "garden": { + "farmingHud": { + "enableHud": true, + "x": 180, + "y": 0 + }, + "dicerTitlePrevent": true, + "visitorHelper": true, + "lockMouseTool": true, + "lockMouseGroundOnly": true + } + }, + "otherLocations": { + "barn": { + "solveHungryHiker": true, + "solveTreasureHunter": true + }, + "rift": { + "mirrorverseWaypoints": true, + "blobbercystGlow": true, + "enigmaSoulWaypoints": false, + "highlightFoundEnigmaSouls": true, + "mcGrubberStacks": 0 + }, + "end": { + "enableEnderNodeHelper": true, + "hudEnabled": true, + "zealotKillsEnabled": true, + "protectorLocationEnabled": true, + "waypoint": true, + "x": 10, + "y": 10 + }, + "spidersDen": { + "relics": { + "enableRelicsHelper": true, + "highlightFoundRelics": false + } + } + }, + "slayers": { + "endermanSlayer": { + "enableYangGlyphsNotification": true, + "highlightBeacons": true, + "highlightNukekubiHeads": true + }, + "vampireSlayer": { + "enableEffigyWaypoints": true, + "compactEffigyWaypoints": false, + "effigyUpdateFrequency": 5, + "enableHolyIceIndicator": true, + "holyIceIndicatorTickDelay": 10, + "holyIceUpdateFrequency": 5, + "enableHealingMelonIndicator": true, + "healingMelonHealthThreshold": 4.0, + "enableSteakStakeIndicator": true, + "steakStakeUpdateFrequency": 5, + "enableManiaIndicator": true, + "maniaUpdateFrequency": 5 + } + }, + "chat": { + "hideAbility": "ACTION_BAR", + "hideHeal": "PASS", + "hideAOTE": "ACTION_BAR", + "hideImplosion": "PASS", + "hideMoltenWave": "PASS", + "hideAds": "FILTER", + "hideTeleportPad": "PASS", + "hideCombo": "PASS", + "hideAutopet": "PASS", + "hideShowOff": "FILTER", + "hideToggleSkyMall": "PASS", + "hideMimicKill": "PASS", + "hideDeath": "PASS", + "hideMana": false, + "hideDicer": "PASS", + "chatRuleConfig": { + "announcementLength": 60, + "announcementScale": 3 + } + }, + "quickNav": { + "enableQuickNav": true, + "button1": { + "render": true, + "uiTitle": "Your Skills", + "clickEvent": "/skills", + "item": { + "id": "diamond_sword", + "count": 1, + "components": "[]" + } + }, + "button2": { + "render": true, + "uiTitle": "Collections", + "clickEvent": "/collection", + "item": { + "id": "painting", + "count": 1, + "components": "[]" + } + }, + "button3": { + "render": true, + "uiTitle": "Pets(:? \\(\\d+\\/\\d+\\))?", + "clickEvent": "/pets", + "item": { + "id": "bone", + "count": 1, + "components": "[]" + } + }, + "button4": { + "render": true, + "uiTitle": "Wardrobe \\([12]/2\\)", + "clickEvent": "/wardrobe", + "item": { + "id": "leather_chestplate", + "count": 1, + "components": "[minecraft:dyed_color\u003d{rgb:8991416}]" + } + }, + "button5": { + "render": true, + "uiTitle": "Sack of Sacks", + "clickEvent": "/sacks", + "item": { + "id": "player_head", + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;-2081424676,-57521078,-2073572414,158072763],name:\"\",properties:[{name:\"textures\",value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0\u003d\"}]}]" + } + }, + "button6": { + "render": true, + "uiTitle": "Storage", + "clickEvent": "/storage", + "item": { + "id": "ender_chest", + "count": 1, + "components": "[]" + } + }, + "button7": { + "render": true, + "uiTitle": "none", + "clickEvent": "/hub", + "item": { + "id": "player_head", + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;-300151517,-631415889,-1193921967,-1821784279],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0\u003d\"}]}]" + } + }, + "button8": { + "render": true, + "uiTitle": "none", + "clickEvent": "/warp dungeon_hub", + "item": { + "id": "player_head", + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;1605800870,415127827,-1236127084,15358548],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0\u003d\"}]}]" + } + }, + "button9": { + "render": true, + "uiTitle": "Visit prtl", + "clickEvent": "/visit prtl", + "item": { + "id": "player_head", + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;-562285948,532499670,-1705302742,775653035],name:\"\",properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0\u003d\"}]}]" + } + }, + "button10": { + "render": true, + "uiTitle": "Enchant Item", + "clickEvent": "/etable", + "item": { + "id": "enchanting_table", + "count": 1, + "components": "[]" + } + }, + "button11": { + "render": true, + "uiTitle": "Anvil", + "clickEvent": "/anvil", + "item": { + "id": "anvil", + "count": 1, + "components": "[]" + } + }, + "button12": { + "render": true, + "uiTitle": "Craft Item", + "clickEvent": "/craft", + "item": { + "id": "crafting_table", + "count": 1, + "components": "[]" + } + } + }, + "misc": { + "richPresence": { + "enableRichPresence": false, + "info": "LOCATION", + "cycleMode": false, + "customMessage": "Playing Skyblock" + } + } +}
\ No newline at end of file diff --git a/src/test/resources/assets/skyblocker/config/skyblocker-v3.json b/src/test/resources/assets/skyblocker/config/skyblocker-v3.json new file mode 100644 index 00000000..d298fff1 --- /dev/null +++ b/src/test/resources/assets/skyblocker/config/skyblocker-v3.json @@ -0,0 +1,573 @@ +{ + "version": 3, + "general": { + "enableTips": true, + "acceptReparty": true, + "shortcuts": { + "enableShortcuts": true, + "enableCommandShortcuts": true, + "enableCommandArgShortcuts": true + }, + "quiverWarning": { + "enableQuiverWarning": true, + "enableQuiverWarningInDungeons": true, + "enableQuiverWarningAfterDungeon": true + }, + "itemList": { + "enableItemList": true + }, + "itemTooltip": { + "enableNPCPrice": true, + "enableMotesPrice": true, + "enableAvgBIN": true, + "avg": "THREE_DAY", + "enableLowestBIN": true, + "enableBazaarPrice": true, + "enableObtainedDate": true, + "enableMuseumInfo": true, + "enableExoticTooltip": true, + "enableAccessoriesHelper": true, + "dungeonQuality": true + }, + "itemInfoDisplay": { + "attributeShardInfo": true, + "itemRarityBackgrounds": true, + "itemRarityBackgroundStyle": "CIRCULAR", + "itemRarityBackgroundsOpacity": 0.5 + }, + "itemProtection": { + "slotLockStyle": "FANCY" + }, + "wikiLookup": { + "enableWikiLookup": true, + "officialWiki": false + }, + "specialEffects": { + "rareDungeonDropEffects": true + }, + "hitbox": { + "oldFarmlandHitbox": true, + "oldLeverHitbox": false + }, + "lockedSlots": [ + 3, + 8, + 0 + ], + "protectedItems": [ + "10f84455-c063-4e3e-b6eb-cc485fe74a06", + "64e491fa-64ed-4abf-829a-940e1fb295d2", + "23a619b6-f487-419e-8598-10b8f21dc80a", + "416aa42d-27d4-4284-913f-d03fa1f528c3", + "4e2a7f16-7d93-4069-bfbd-a8ff8e42f285", + "63ec4f84-2005-45e5-a4b7-0c9ae84dc612", + "aba2b017-91e8-4263-ae9e-5a10f8dc91fe", + "f1391873-5126-496d-b0fe-c861faeaf681", + "bb84cfea-3fdd-4579-b5fa-907cd8123593", + "a604f533-49f6-4190-b971-08f725231229", + "333cba8d-3136-4494-9fa0-40dff482ef66", + "91f49338-fc0b-4601-a704-aabc44853318", + "d76e5eca-be47-4d0b-973c-3ec30fd176dc", + "301c58a7-9b8a-4c4e-9e8d-3e8a9471a916", + "c8ab629e-fc5b-4af0-8053-acb155411c9b", + "333a079f-93d4-48f1-af9f-862b1ecc417c", + "0843d8b0-b6a6-47fc-afde-8eee8c5455d9", + "8bcf2a99-68e6-4646-ac04-4c72e55c6818", + "214333db-d71c-4080-8487-00b4666bb9a4", + "1eba38b5-e0f6-4862-bf19-e2df33e21ce4", + "6b42c77f-1891-43a4-a9ea-4454e299a059", + "71ce1812-d1fd-4e71-98ad-8b863759a82f", + "253489d9-49d0-46c8-b3f8-b70c3663c5d2", + "6033eff2-879d-494f-8dae-696fc8a3c82e", + "5f6090c8-cbc4-4030-97ac-5018c151cc15", + "64162e02-91fb-4ec3-a57c-866436a99a7a", + "a299c718-c66b-4de5-9747-71c172ac4601", + "e21172ce-0a41-426a-a445-84a28677cd65", + "4bfd23bf-ceb8-4951-874d-e61633e088d8", + "575ce2c9-251e-4435-9020-de1a2e24b1d0", + "b06b8fe2-470a-43f3-b844-658472f20996", + "e54dea3d-ca45-451e-9ae4-0e5fb87e97b2", + "67b47348-da46-4257-833a-dd23fb074cc6", + "9f7597ec-c3c9-46df-9f36-8e76c8745a9d", + "e297dcef-2f3d-4fb3-9781-aaff6017178c", + "ad9a598d-e5c9-4293-92c8-5e41bebe913c", + "fa51c660-b262-4826-bed8-6138236919cb" + ], + "customItemNames": {}, + "customDyeColors": {}, + "customArmorTrims": { + "f1391873-5126-496d-b0fe-c861faeaf681": { + "material": "minecraft:amethyst", + "pattern": "minecraft:raiser" + }, + "81409da1-610c-445a-aba2-4c95a3cabbf2": { + "material": "minecraft:amethyst", + "pattern": "minecraft:coast" + } + }, + "customAnimatedDyes": { + "f1391873-5126-496d-b0fe-c861faeaf681": { + "color1": 16711680, + "color2": 11154282, + "samples": 10, + "cycleBack": true, + "tickDelay": 4 + } + } + }, + "uiAndVisuals": { + "compactorDeletorPreview": true, + "dontStripSkinAlphaValues": true, + "backpackPreviewWithoutShift": true, + "fancyCraftingTable": true, + "hideStatusEffectOverlay": false, + "chestValue": { + "enableChestValue": true, + "color": "DARK_GREEN", + "incompleteColor": "BLUE" + }, + "itemCooldown": { + "enableItemCooldowns": true + }, + "titleContainer": { + "titleContainerScale": 100.0, + "x": 482, + "y": 170, + "direction": "VERTICAL", + "alignment": "MIDDLE" + }, + "tabHud": { + "tabHudEnabled": true, + "tabHudScale": 100, + "enableHudBackground": true, + "plainPlayerNames": false, + "nameSorting": "ALPHABETICAL" + }, + "fancyAuctionHouse": { + "enabled": true, + "highlightCheapBIN": true + }, + "bars": { + "enableBars": true, + "barPositions": { + "healthBarPosition": "LAYER1", + "manaBarPosition": "LAYER1", + "defenceBarPosition": "RIGHT", + "experienceBarPosition": "RIGHT" + } + }, + "waypoints": { + "enableWaypoints": true, + "waypointType": "WAYPOINT" + }, + "teleportOverlay": { + "enableTeleportOverlays": true, + "enableWeirdTransmission": true, + "enableInstantTransmission": true, + "enableEtherTransmission": true, + "enableSinrecallTransmission": true, + "enableWitherImpact": true + }, + "searchOverlay": { + "enableBazaar": true, + "enableAuctionHouse": true, + "keepPreviousSearches": false, + "maxSuggestions": 5, + "historyLength": 5, + "enableCommands": false, + "bazaarHistory": [ + "Enchanted Snow Block", + "Recombobulator 3000", + "Enchanted Snow Block", + "Recombobulator 3000", + "Enchanted Snow Block" + ], + "auctionHistory": [ + "God Potion", + "New Year Cake (Year 30", + "New Year Cake (Year 31", + "New Year Cake (Year 32", + "New Year Cake (Year 2" + ] + }, + "flameOverlay": { + "flameHeight": 0, + "flameOpacity": 0 + }, + "hideEmptyTooltips": true + }, + "helpers": { + "enableNewYearCakesHelper": true, + "mythologicalRitual": { + "enableMythologicalRitualHelper": true + }, + "experiments": { + "enableChronomatronSolver": true, + "enableSuperpairsSolver": true, + "enableUltrasequencerSolver": true + }, + "fishing": { + "enableFishingHelper": true, + "enableFishingTimer": false, + "changeTimerColor": true, + "fishingTimerScale": 1.0, + "hideOtherPlayersRods": false + }, + "fairySouls": { + "enableFairySoulsHelper": true, + "highlightFoundSouls": false, + "highlightOnlyNearbySouls": false + } + }, + "dungeons": { + "fancyPartyFinder": true, + "croesusHelper": true, + "playerSecretsTracker": true, + "starredMobGlow": true, + "starredMobBoundingBoxes": true, + "allowDroppingProtectedItems": false, + "dungeonMap": { + "enableMap": true, + "mapScaling": 1.0, + "mapX": 2, + "mapY": 2 + }, + "puzzleSolvers": { + "solveThreeWeirdos": true, + "blazeSolver": true, + "creeperSolver": true, + "solveTrivia": true, + "solveTicTacToe": true, + "solveWaterboard": true, + "solveBoulder": true, + "solveIceFill": true, + "solveSilverfish": true + }, + "theProfessor": { + "fireFreezeStaffTimer": true, + "floor3GuardianHealthDisplay": true + }, + "livid": { + "enableLividColorGlow": true, + "enableLividColorText": true, + "enableLividColorTitle": true, + "lividColorText": "[color] is sus" + }, + "terminals": { + "solveColor": true, + "solveOrder": true, + "solveStartsWith": true + }, + "secretWaypoints": { + "enableRoomMatching": true, + "enableSecretWaypoints": true, + "waypointType": "OUTLINED_HIGHLIGHT", + "showSecretText": true, + "enableEntranceWaypoints": true, + "enableSuperboomWaypoints": true, + "enableChestWaypoints": true, + "enableItemWaypoints": true, + "enableBatWaypoints": true, + "enableWitherWaypoints": true, + "enableLeverWaypoints": true, + "enableFairySoulWaypoints": true, + "enableStonkWaypoints": false, + "enableAotvWaypoints": true, + "enablePearlWaypoints": true, + "enableDefaultWaypoints": true + }, + "mimicMessage": { + "sendMimicMessage": true, + "mimicMessage": "Mimic dead!" + }, + "doorHighlight": { + "enableDoorHighlight": true, + "doorHighlightType": "OUTLINED_HIGHLIGHT" + }, + "dungeonScore": { + "enableDungeonScore270Message": false, + "enableDungeonScore270Title": false, + "enableDungeonScore270Sound": false, + "dungeonScore270Message": "270 Score Reached!", + "enableDungeonScore300Message": true, + "enableDungeonScore300Title": true, + "enableDungeonScore300Sound": true, + "dungeonScore300Message": "[做得好] 300 Social Credit has been added to your account. Enjoy your boss fight.", + "enableDungeonCryptsMessage": true, + "dungeonCryptsMessageThreshold": 250, + "dungeonCryptsMessage": "We only have [crypts] crypts out of 5, we need more!", + "enableScoreHUD": true, + "scoreX": 25, + "scoreY": 134, + "scoreScaling": 1.0 + }, + "dungeonChestProfit": { + "enableProfitCalculator": true, + "includeKismet": true, + "includeEssence": false, + "croesusProfit": true, + "neutralThreshold": 1000, + "neutralColor": "DARK_GRAY", + "profitColor": "DARK_GREEN", + "lossColor": "RED", + "incompleteColor": "BLUE" + } + }, + "crimsonIsle": { + "kuudra": { + "supplyWaypoints": true, + "fuelWaypoints": true, + "suppliesAndFuelWaypointType": "WAYPOINT", + "ballistaBuildWaypoints": true, + "safeSpotWaypoints": true, + "pearlWaypoints": true, + "noArrowPoisonWarning": true, + "arrowPoisonThreshold": 16 + } + }, + "mining": { + "enableDrillFuel": true, + "dwarvenMines": { + "solveFetchur": true, + "solvePuzzler": true + }, + "dwarvenHud": { + "enabledCommissions": true, + "enabledPowder": true, + "style": "SIMPLE", + "commissionsX": 10, + "commissionsY": 10, + "powderX": 10, + "powderY": 70 + }, + "crystalsHud": { + "enabled": true, + "showLocations": true, + "locationSize": 8, + "x": 10, + "y": 130, + "mapScaling": 1.0 + }, + "crystalsWaypoints": { + "enabled": true, + "findInChat": true + }, + "crystalHollows": { + "metalDetectorHelper": true + } + }, + "farming": { + "garden": { + "farmingHud": { + "enableHud": true, + "x": 180, + "y": 0 + }, + "dicerTitlePrevent": true, + "visitorHelper": true, + "lockMouseTool": true, + "lockMouseGroundOnly": true + } + }, + "otherLocations": { + "barn": { + "solveHungryHiker": true, + "solveTreasureHunter": true + }, + "rift": { + "mirrorverseWaypoints": true, + "blobbercystGlow": true, + "enigmaSoulWaypoints": false, + "highlightFoundEnigmaSouls": true, + "mcGrubberStacks": 0 + }, + "end": { + "enableEnderNodeHelper": true, + "hudEnabled": true, + "zealotKillsEnabled": true, + "protectorLocationEnabled": true, + "waypoint": true, + "x": 10, + "y": 10 + }, + "spidersDen": { + "relics": { + "enableRelicsHelper": true, + "highlightFoundRelics": false + } + } + }, + "slayers": { + "endermanSlayer": { + "enableYangGlyphsNotification": true, + "highlightBeacons": true, + "highlightNukekubiHeads": true + }, + "vampireSlayer": { + "enableEffigyWaypoints": true, + "compactEffigyWaypoints": false, + "effigyUpdateFrequency": 5, + "enableHolyIceIndicator": true, + "holyIceIndicatorTickDelay": 10, + "holyIceUpdateFrequency": 5, + "enableHealingMelonIndicator": true, + "healingMelonHealthThreshold": 4.0, + "enableSteakStakeIndicator": true, + "steakStakeUpdateFrequency": 5, + "enableManiaIndicator": true, + "maniaUpdateFrequency": 5 + } + }, + "chat": { + "hideAbility": "ACTION_BAR", + "hideHeal": "PASS", + "hideAOTE": "ACTION_BAR", + "hideImplosion": "PASS", + "hideMoltenWave": "PASS", + "hideAds": "FILTER", + "hideTeleportPad": "PASS", + "hideCombo": "PASS", + "hideAutopet": "PASS", + "hideShowOff": "FILTER", + "hideToggleSkyMall": "PASS", + "hideMimicKill": "PASS", + "hideDeath": "PASS", + "hideMana": false, + "hideDicer": "PASS", + "chatRuleConfig": { + "announcementLength": 60, + "announcementScale": 3 + } + }, + "quickNav": { + "enableQuickNav": true, + "button8": { + "render": true, + "uiTitle": "none", + "clickEvent": "/hub", + "itemData": { + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;-300151517,-631415889,-1193921967,-1821784279],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0\u003d\"}]}]", + "item": "minecraft:player_head" + } + }, + "button9": { + "render": true, + "uiTitle": "none", + "clickEvent": "/warp dungeon_hub", + "itemData": { + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;1605800870,415127827,-1236127084,15358548],name:\"\",properties:[{name:\"textures\",value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0\u003d\"}]}]", + "item": "minecraft:player_head" + } + }, + "button3": { + "render": true, + "uiTitle": "Pets(:? \\(\\d+\\/\\d+\\))?", + "clickEvent": "/pets", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:bone" + } + }, + "button4": { + "render": true, + "uiTitle": "Wardrobe \\([12]/2\\)", + "clickEvent": "/wardrobe", + "itemData": { + "count": 1, + "components": "[minecraft:dyed_color\u003d{rgb:8991416}]", + "item": "minecraft:leather_chestplate" + } + }, + "button5": { + "render": true, + "uiTitle": "Sack of Sacks", + "clickEvent": "/sacks", + "itemData": { + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;-2081424676,-57521078,-2073572414,158072763],name:\"\",properties:[{name:\"textures\",value:\"ewogICJ0aW1lc3RhbXAiIDogMTU5MTMxMDU4NTYwOSwKICAicHJvZmlsZUlkIiA6ICI0MWQzYWJjMmQ3NDk0MDBjOTA5MGQ1NDM0ZDAzODMxYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZWdha2xvb24iLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODBhMDc3ZTI0OGQxNDI3NzJlYTgwMDg2NGY4YzU3OGI5ZDM2ODg1YjI5ZGFmODM2YjY0YTcwNjg4MmI2ZWMxMCIKICAgIH0KICB9Cn0\u003d\"}]}]", + "item": "minecraft:player_head" + } + }, + "button6": { + "render": true, + "uiTitle": "Storage", + "clickEvent": "/storage", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:ender_chest" + } + }, + "button1": { + "render": true, + "uiTitle": "Your Skills", + "clickEvent": "/skills", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:diamond_sword" + } + }, + "button2": { + "render": true, + "uiTitle": "Collections", + "clickEvent": "/collection", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:painting" + } + }, + "button11": { + "render": true, + "uiTitle": "Enchant Item", + "clickEvent": "/etable", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:enchanting_table" + } + }, + "button10": { + "render": true, + "uiTitle": "Visit prtl", + "clickEvent": "/visit prtl", + "itemData": { + "count": 1, + "components": "[minecraft:profile\u003d{id:[I;-562285948,532499670,-1705302742,775653035],name:\"\",properties:[{name:\"textures\",value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjVkZjU1NTkyNjQzMGQ1ZDc1YWRlZDIxZGQ5NjE5Yjc2YzViN2NhMmM3ZjU0MDE0NDA1MjNkNTNhOGJjZmFhYiJ9fX0\u003d\"}]}]", + "item": "minecraft:player_head" + } + }, + "button13": { + "render": true, + "uiTitle": "Craft Item", + "clickEvent": "/craft", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:crafting_table" + } + }, + "button12": { + "render": true, + "uiTitle": "Anvil", + "clickEvent": "/anvil", + "itemData": { + "count": 1, + "components": "[]", + "item": "minecraft:anvil" + } + } + }, + "misc": { + "richPresence": { + "enableRichPresence": false, + "info": "LOCATION", + "cycleMode": false, + "customMessage": "Playing Skyblock" + } + } +}
\ No newline at end of file |