diff options
Diffstat (limited to 'src/main/java')
52 files changed, 1890 insertions, 798 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) { |