From 0de355c3ccab5c66db9441d94d9eb84defb3fdb9 Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Thu, 28 Sep 2023 16:03:00 +0200 Subject: Implement item cooldown display --- .../java/me/xmrvizzy/skyblocker/SkyblockerMod.java | 2 + .../skyblocker/config/SkyblockerConfig.java | 8 ++ .../events/ClientPlayerBlockBreakEvent.java | 21 +++++ .../mixin/ClientPlayerInteractionManagerMixin.java | 24 ++++++ .../skyblocker/mixin/DrawContextMixin.java | 61 ++++++++------ .../xmrvizzy/skyblocker/mixin/InGameHudMixin.java | 25 +++++- .../skyblock/cooldown/ItemCooldownEntry.java | 24 ++++++ .../skyblock/cooldown/ItemCooldowns.java | 96 ++++++++++++++++++++++ .../me/xmrvizzy/skyblocker/utils/ItemUtils.java | 15 ++++ src/main/resources/skyblocker.mixins.json | 7 +- 10 files changed, 253 insertions(+), 30 deletions(-) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java create mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java index b28ad3d4..1d6f1eed 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java @@ -4,6 +4,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.skyblock.*; +import me.xmrvizzy.skyblocker.skyblock.cooldown.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.dungeon.*; import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud; @@ -78,6 +79,7 @@ public class SkyblockerMod implements ClientModInitializer { Relics.init(); BackpackPreview.init(); QuickNav.init(); + ItemCooldowns.init(); DwarvenHud.init(); ChatMessageListener.init(); Shortcuts.init(); diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java index 5848ed15..092bc587 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -187,6 +187,10 @@ public class SkyblockerConfig implements ConfigData { @ConfigEntry.Gui.CollapsibleObject() public FairySouls fairySouls = new FairySouls(); + @ConfigEntry.Category("itemCooldown") + @ConfigEntry.Gui.CollapsibleObject() + public ItemCooldown itemCooldown = new ItemCooldown(); + @ConfigEntry.Category("shortcuts") @ConfigEntry.Gui.CollapsibleObject() public Shortcuts shortcuts = new Shortcuts(); @@ -320,6 +324,10 @@ public class SkyblockerConfig implements ConfigData { public boolean highlightOnlyNearbySouls = false; } + public static class ItemCooldown { + public boolean enableItemCooldowns = true; + } + public static class Shortcuts { @ConfigEntry.Gui.Tooltip() public boolean enableShortcuts = true; diff --git a/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java new file mode 100644 index 00000000..9d9463f7 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java @@ -0,0 +1,21 @@ +package me.xmrvizzy.skyblocker.events; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.BlockPos; + +// Fabric API currently doesn't have an event for this +public class ClientPlayerBlockBreakEvent { + public static final Event AFTER = EventFactory.createArrayBacked(AfterBlockBreak.class, + (listeners) -> (pos, player) -> { + for (AfterBlockBreak listener : listeners) { + listener.afterBlockBreak(pos, player); + } + }); + + @FunctionalInterface + public interface AfterBlockBreak { + void afterBlockBreak(BlockPos pos, PlayerEntity player); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..4b343317 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,24 @@ +package me.xmrvizzy.skyblocker.mixin; + +import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +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.CallbackInfoReturnable; + +@Mixin(ClientPlayerInteractionManager.class) +public class ClientPlayerInteractionManagerMixin { + @Shadow + @Final + private MinecraftClient client; + + @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V")) + private void skyblocker$blockBreak(BlockPos pos, CallbackInfoReturnable cir) { + ClientPlayerBlockBreakEvent.AFTER.invoker().afterBlockBreak(pos, this.client.player); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java index cfe979d0..8cde4973 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java @@ -1,12 +1,13 @@ package me.xmrvizzy.skyblocker.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalRef; import com.mojang.blaze3d.systems.RenderSystem; - import dev.cbyrne.betterinject.annotations.Arg; import dev.cbyrne.betterinject.annotations.Inject; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; +import me.xmrvizzy.skyblocker.skyblock.cooldown.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.item.AttributeShards; import me.xmrvizzy.skyblocker.utils.ItemUtils; import me.xmrvizzy.skyblocker.utils.Utils; @@ -18,7 +19,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.util.Formatting; import net.minecraft.util.math.ColorHelper; - import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -73,7 +73,8 @@ public abstract class DrawContextMixin { max = Integer.parseInt(split[1]) * 1000; } break; - } else if (line.contains("uses.")) { + } + else if (line.contains("uses.")) { if (clearFormatting != null) { int startIndex = clearFormatting.lastIndexOf("after") + 6; int endIndex = clearFormatting.indexOf("uses", startIndex); @@ -99,35 +100,45 @@ public abstract class DrawContextMixin { @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD")) private void skyblocker$renderAttributeShardDisplay(@Arg TextRenderer textRenderer, @Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y, @Local(argsOnly = true) LocalRef countOverride) { - if (!SkyblockerConfig.get().general.itemInfoDisplay.attributeShardInfo) return; + if (!SkyblockerConfig.get().general.itemInfoDisplay.attributeShardInfo) return; + + NbtCompound nbt = stack.getNbt(); + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - NbtCompound nbt = stack.getNbt(); + if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) { + NbtCompound attributesTag = extraAttributes.getCompound("attributes"); + String[] attributes = attributesTag.getKeys().toArray(String[]::new); - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + if (attributes.length != 0) { + String attributeId = attributes[0]; + int attributeLevel = attributesTag.getInt(attributeId); - if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) { - NbtCompound attributesTag = extraAttributes.getCompound("attributes"); - String[] attributes = attributesTag.getKeys().toArray(String[]::new); + //Set item count + countOverride.set(Integer.toString(attributeLevel)); - if (attributes.length != 0) { - String attributeId = attributes[0]; - int attributeLevel = attributesTag.getInt(attributeId); + //Draw the attribute name + this.matrices.push(); + this.matrices.translate(0f, 0f, 200f); - //Set item count - countOverride.set(Integer.toString(attributeLevel)); + String attributeInitials = AttributeShards.getShortName(attributeId); - //Draw the attribute name - this.matrices.push(); - this.matrices.translate(0f, 0f, 200f); + this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true); - String attributeInitials = AttributeShards.getShortName(attributeId); + this.matrices.pop(); + } + } + } + } - this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true); + @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) + private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local ItemStack stack) { + if (Utils.isOnSkyblock() && ItemCooldowns.isItemOnCooldown(stack)) { + return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); + } - this.matrices.pop(); - } - } - } + return cooldownProgress; } -} \ No newline at end of file +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java index 752b102a..19f928d8 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java @@ -1,17 +1,22 @@ package me.xmrvizzy.skyblocker.mixin; +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.sugar.Local; import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars; import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; +import me.xmrvizzy.skyblocker.skyblock.cooldown.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap; import me.xmrvizzy.skyblocker.utils.Utils; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.item.ItemStack; import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -32,6 +37,10 @@ public abstract class InGameHudMixin { @Shadow private int scaledWidth; + @Shadow + @Final + private MinecraftClient client; + @Inject(method = "renderHotbar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHotbarItem(Lnet/minecraft/client/gui/DrawContext;IIFLnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;I)V", ordinal = 0)) public void skyblocker$renderHotbarItemLock(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y) { if (Utils.isOnSkyblock() && HotbarSlotLock.isLocked(index)) { @@ -61,9 +70,21 @@ public abstract class InGameHudMixin { if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.bars.enableBars && !Utils.isInTheRift()) ci.cancel(); } - + @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"), cancellable = true) private void skyblocker$dontRenderStatusEffects(CallbackInfo ci) { if (Utils.isOnSkyblock() && SkyblockerConfig.get().general.hideStatusEffectOverlay) ci.cancel(); } -} \ No newline at end of file + + @ModifyExpressionValue(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getAttackCooldownProgress(F)F")) + private float skyblocker$modifyAttackIndicatorCooldown(float cooldownProgress) { + if (Utils.isOnSkyblock()) { + ItemStack stack = this.client.player.getMainHandStack(); + if (ItemCooldowns.isItemOnCooldown(stack)) { + return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); + } + } + + return cooldownProgress; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java new file mode 100644 index 00000000..0b21c75f --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java @@ -0,0 +1,24 @@ +package me.xmrvizzy.skyblocker.skyblock.cooldown; + +public class ItemCooldownEntry { + private final int cooldown; + private final long startTime; + + public ItemCooldownEntry(int cooldown) { + this.cooldown = cooldown; + this.startTime = System.currentTimeMillis(); + } + + public boolean isOnCooldown() { + return (this.startTime + this.cooldown) > System.currentTimeMillis(); + } + + public long getRemainingCooldown() { + long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); + return time <= 0 ? 0 : time; + } + + public float getRemainingCooldownPercent() { + return this.isOnCooldown() ? ((float) this.getRemainingCooldown()) / ((float) cooldown) : 0.0f; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java new file mode 100644 index 00000000..6663b368 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java @@ -0,0 +1,96 @@ +package me.xmrvizzy.skyblocker.skyblock.cooldown; + +import com.google.common.collect.ImmutableList; +import me.xmrvizzy.skyblocker.config.SkyblockerConfig; +import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; +import me.xmrvizzy.skyblocker.utils.ItemUtils; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.HashMap; +import java.util.Map; + +public class ItemCooldowns { + private static final String JUNGLE_AXE_ID = "JUNGLE_AXE"; + private static final String TREECAPITATOR_ID = "TREECAPITATOR_AXE"; + private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; + private static final ImmutableList BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); + + private static final Map itemCooldowns = new HashMap<>(); + private static SkyblockerConfig.ItemCooldown config; + + public static void init() { + ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); + UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); + config = SkyblockerConfig.get().general.itemCooldown; + } + + public static void afterBlockBreak(BlockPos pos, PlayerEntity player) { + if (!config.enableItemCooldowns) return; + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId == null) return; + + if (usedItemId.equals(JUNGLE_AXE_ID)) { + if (!isItemOnCooldown(JUNGLE_AXE_ID)) { + itemCooldowns.put(JUNGLE_AXE_ID, new ItemCooldownEntry(2000)); + } + } + else if (usedItemId.equals(TREECAPITATOR_ID)) { + if (!isItemOnCooldown(TREECAPITATOR_ID)) { + itemCooldowns.put(TREECAPITATOR_ID, new ItemCooldownEntry(2000)); + } + } + } + + private static TypedActionResult onItemInteract(PlayerEntity player, World world, Hand hand) { + if (!config.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { + if (!isItemOnCooldown(GRAPPLING_HOOK_ID) && !isPlayerWearingBatArmor(player)) { + itemCooldowns.put(GRAPPLING_HOOK_ID, new ItemCooldownEntry(2000)); + } + } + + return TypedActionResult.pass(ItemStack.EMPTY); + } + + public static boolean isItemOnCooldown(ItemStack itemStack) { + return isItemOnCooldown(ItemUtils.getItemId(itemStack)); + } + + private static boolean isItemOnCooldown(String itemId) { + if (itemCooldowns.containsKey(itemId)) { + ItemCooldownEntry cooldownEntry = itemCooldowns.get(itemId); + if (cooldownEntry.isOnCooldown()) { + return true; + } + else { + itemCooldowns.remove(cooldownEntry); + return false; + } + } + + return false; + } + + public static ItemCooldownEntry getItemCooldownEntry(ItemStack itemStack) { + return itemCooldowns.get(ItemUtils.getItemId(itemStack)); + } + + private static boolean isPlayerWearingBatArmor(PlayerEntity player) { + for (ItemStack stack : player.getArmorItems()) { + String itemId = ItemUtils.getItemId(stack); + if (!BAT_ARMOR_IDS.contains(itemId)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java index 5c12b777..9953edae 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java @@ -4,6 +4,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.client.MinecraftClient; import net.minecraft.client.item.TooltipContext; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.StringNbtReader; import net.minecraft.text.Text; @@ -40,4 +41,18 @@ public class ItemUtils { throw new RuntimeException(e); } } + + public static String getItemId(ItemStack itemStack) { + if (itemStack == null) return null; + + NbtCompound nbt = itemStack.getNbt(); + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + if (extraAttributes.contains("id")) { + return extraAttributes.getString("id"); + } + } + + return null; + } } diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index a4fe92dd..cb8635e3 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -8,6 +8,7 @@ "ArmorTrimMixin", "BatEntityMixin", "ClientPlayerEntityMixin", + "ClientPlayerInteractionManagerMixin", "ClientPlayNetworkHandlerMixin", "DrawContextMixin", "DyeableItemMixin", @@ -27,15 +28,15 @@ "YggdrasilMinecraftSessionServiceMixin", "YggdrasilServicesKeyInfoMixin", "accessor.BeaconBlockEntityRendererInvoker", + "accessor.DrawContextInvoker", "accessor.FrustumInvoker", "accessor.HandledScreenAccessor", "accessor.PlayerListHudAccessor", "accessor.RecipeBookWidgetAccessor", "accessor.ScreenAccessor", - "accessor.WorldRendererAccessor", - "accessor.DrawContextInvoker" + "accessor.WorldRendererAccessor" ], "injectors": { "defaultRequire": 1 } -} \ No newline at end of file +} -- cgit From 23e8e8a703db8f7bfda09a0c57919aa608631e8c Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:49:50 +0200 Subject: Revert some unnecessary code style changes --- .../skyblocker/mixin/DrawContextMixin.java | 45 +++++++++++----------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java index 8cde4973..f35cc169 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java @@ -73,8 +73,7 @@ public abstract class DrawContextMixin { max = Integer.parseInt(split[1]) * 1000; } break; - } - else if (line.contains("uses.")) { + } else if (line.contains("uses.")) { if (clearFormatting != null) { int startIndex = clearFormatting.lastIndexOf("after") + 6; int endIndex = clearFormatting.indexOf("uses", startIndex); @@ -100,36 +99,36 @@ public abstract class DrawContextMixin { @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD")) private void skyblocker$renderAttributeShardDisplay(@Arg TextRenderer textRenderer, @Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y, @Local(argsOnly = true) LocalRef countOverride) { - if (!SkyblockerConfig.get().general.itemInfoDisplay.attributeShardInfo) return; + if (!SkyblockerConfig.get().general.itemInfoDisplay.attributeShardInfo) return; - NbtCompound nbt = stack.getNbt(); + NbtCompound nbt = stack.getNbt(); - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) { - NbtCompound attributesTag = extraAttributes.getCompound("attributes"); - String[] attributes = attributesTag.getKeys().toArray(String[]::new); + if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) { + NbtCompound attributesTag = extraAttributes.getCompound("attributes"); + String[] attributes = attributesTag.getKeys().toArray(String[]::new); - if (attributes.length != 0) { - String attributeId = attributes[0]; - int attributeLevel = attributesTag.getInt(attributeId); + if (attributes.length != 0) { + String attributeId = attributes[0]; + int attributeLevel = attributesTag.getInt(attributeId); - //Set item count - countOverride.set(Integer.toString(attributeLevel)); + //Set item count + countOverride.set(Integer.toString(attributeLevel)); - //Draw the attribute name - this.matrices.push(); - this.matrices.translate(0f, 0f, 200f); + //Draw the attribute name + this.matrices.push(); + this.matrices.translate(0f, 0f, 200f); - String attributeInitials = AttributeShards.getShortName(attributeId); + String attributeInitials = AttributeShards.getShortName(attributeId); - this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true); + this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true); - this.matrices.pop(); - } - } - } + this.matrices.pop(); + } + } + } } @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", -- cgit From 3cc5f282d65d37c66a8c93035da38210c42fcb1d Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Fri, 29 Sep 2023 16:58:26 +0200 Subject: Move code into the skyblock.item package --- .../java/me/xmrvizzy/skyblocker/SkyblockerMod.java | 2 +- .../skyblocker/mixin/DrawContextMixin.java | 2 +- .../xmrvizzy/skyblocker/mixin/InGameHudMixin.java | 2 +- .../skyblock/cooldown/ItemCooldownEntry.java | 24 ----- .../skyblock/cooldown/ItemCooldowns.java | 96 ----------------- .../skyblocker/skyblock/item/ItemCooldowns.java | 119 +++++++++++++++++++++ 6 files changed, 122 insertions(+), 123 deletions(-) delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java index 1d6f1eed..18327bde 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java @@ -4,7 +4,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.skyblock.*; -import me.xmrvizzy.skyblocker.skyblock.cooldown.ItemCooldowns; +import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.dungeon.*; import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud; diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java index f35cc169..69fe48a0 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java @@ -7,7 +7,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import dev.cbyrne.betterinject.annotations.Arg; import dev.cbyrne.betterinject.annotations.Inject; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.skyblock.cooldown.ItemCooldowns; +import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.item.AttributeShards; import me.xmrvizzy.skyblocker.utils.ItemUtils; import me.xmrvizzy.skyblocker.utils.Utils; diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java index 19f928d8..9171557f 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java @@ -6,7 +6,7 @@ import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars; import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; -import me.xmrvizzy.skyblocker.skyblock.cooldown.ItemCooldowns; +import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap; import me.xmrvizzy.skyblocker.utils.Utils; import net.fabricmc.api.EnvType; diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java deleted file mode 100644 index 0b21c75f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldownEntry.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.cooldown; - -public class ItemCooldownEntry { - private final int cooldown; - private final long startTime; - - public ItemCooldownEntry(int cooldown) { - this.cooldown = cooldown; - this.startTime = System.currentTimeMillis(); - } - - public boolean isOnCooldown() { - return (this.startTime + this.cooldown) > System.currentTimeMillis(); - } - - public long getRemainingCooldown() { - long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); - return time <= 0 ? 0 : time; - } - - public float getRemainingCooldownPercent() { - return this.isOnCooldown() ? ((float) this.getRemainingCooldown()) / ((float) cooldown) : 0.0f; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java deleted file mode 100644 index 6663b368..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/cooldown/ItemCooldowns.java +++ /dev/null @@ -1,96 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.cooldown; - -import com.google.common.collect.ImmutableList; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; -import me.xmrvizzy.skyblocker.utils.ItemUtils; -import net.fabricmc.fabric.api.event.player.UseItemCallback; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Hand; -import net.minecraft.util.TypedActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - -import java.util.HashMap; -import java.util.Map; - -public class ItemCooldowns { - private static final String JUNGLE_AXE_ID = "JUNGLE_AXE"; - private static final String TREECAPITATOR_ID = "TREECAPITATOR_AXE"; - private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; - private static final ImmutableList BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); - - private static final Map itemCooldowns = new HashMap<>(); - private static SkyblockerConfig.ItemCooldown config; - - public static void init() { - ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); - UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); - config = SkyblockerConfig.get().general.itemCooldown; - } - - public static void afterBlockBreak(BlockPos pos, PlayerEntity player) { - if (!config.enableItemCooldowns) return; - - String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); - if (usedItemId == null) return; - - if (usedItemId.equals(JUNGLE_AXE_ID)) { - if (!isItemOnCooldown(JUNGLE_AXE_ID)) { - itemCooldowns.put(JUNGLE_AXE_ID, new ItemCooldownEntry(2000)); - } - } - else if (usedItemId.equals(TREECAPITATOR_ID)) { - if (!isItemOnCooldown(TREECAPITATOR_ID)) { - itemCooldowns.put(TREECAPITATOR_ID, new ItemCooldownEntry(2000)); - } - } - } - - private static TypedActionResult onItemInteract(PlayerEntity player, World world, Hand hand) { - if (!config.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); - - String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); - if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { - if (!isItemOnCooldown(GRAPPLING_HOOK_ID) && !isPlayerWearingBatArmor(player)) { - itemCooldowns.put(GRAPPLING_HOOK_ID, new ItemCooldownEntry(2000)); - } - } - - return TypedActionResult.pass(ItemStack.EMPTY); - } - - public static boolean isItemOnCooldown(ItemStack itemStack) { - return isItemOnCooldown(ItemUtils.getItemId(itemStack)); - } - - private static boolean isItemOnCooldown(String itemId) { - if (itemCooldowns.containsKey(itemId)) { - ItemCooldownEntry cooldownEntry = itemCooldowns.get(itemId); - if (cooldownEntry.isOnCooldown()) { - return true; - } - else { - itemCooldowns.remove(cooldownEntry); - return false; - } - } - - return false; - } - - public static ItemCooldownEntry getItemCooldownEntry(ItemStack itemStack) { - return itemCooldowns.get(ItemUtils.getItemId(itemStack)); - } - - private static boolean isPlayerWearingBatArmor(PlayerEntity player) { - for (ItemStack stack : player.getArmorItems()) { - String itemId = ItemUtils.getItemId(stack); - if (!BAT_ARMOR_IDS.contains(itemId)) { - return false; - } - } - return true; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java new file mode 100644 index 00000000..6569e04c --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java @@ -0,0 +1,119 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import com.google.common.collect.ImmutableList; +import me.xmrvizzy.skyblocker.config.SkyblockerConfig; +import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; +import me.xmrvizzy.skyblocker.utils.ItemUtils; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.HashMap; +import java.util.Map; + +public class ItemCooldowns { + private static final String JUNGLE_AXE_ID = "JUNGLE_AXE"; + private static final String TREECAPITATOR_ID = "TREECAPITATOR_AXE"; + private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; + private static final ImmutableList BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); + + private static final Map itemCooldowns = new HashMap<>(); + private static SkyblockerConfig.ItemCooldown config; + + public static void init() { + ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); + UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); + config = SkyblockerConfig.get().general.itemCooldown; + } + + public static void afterBlockBreak(BlockPos pos, PlayerEntity player) { + if (!config.enableItemCooldowns) return; + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId == null) return; + + if (usedItemId.equals(JUNGLE_AXE_ID)) { + if (!isItemOnCooldown(JUNGLE_AXE_ID)) { + itemCooldowns.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); + } + } + else if (usedItemId.equals(TREECAPITATOR_ID)) { + if (!isItemOnCooldown(TREECAPITATOR_ID)) { + itemCooldowns.put(TREECAPITATOR_ID, new CooldownEntry(2000)); + } + } + } + + private static TypedActionResult onItemInteract(PlayerEntity player, World world, Hand hand) { + if (!config.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { + if (!isItemOnCooldown(GRAPPLING_HOOK_ID) && !isPlayerWearingBatArmor(player)) { + itemCooldowns.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); + } + } + + return TypedActionResult.pass(ItemStack.EMPTY); + } + + public static boolean isItemOnCooldown(ItemStack itemStack) { + return isItemOnCooldown(ItemUtils.getItemId(itemStack)); + } + + private static boolean isItemOnCooldown(String itemId) { + if (itemCooldowns.containsKey(itemId)) { + CooldownEntry cooldownEntry = itemCooldowns.get(itemId); + if (cooldownEntry.isOnCooldown()) { + return true; + } + else { + itemCooldowns.remove(cooldownEntry); + return false; + } + } + + return false; + } + + public static CooldownEntry getItemCooldownEntry(ItemStack itemStack) { + return itemCooldowns.get(ItemUtils.getItemId(itemStack)); + } + + private static boolean isPlayerWearingBatArmor(PlayerEntity player) { + for (ItemStack stack : player.getArmorItems()) { + String itemId = ItemUtils.getItemId(stack); + if (!BAT_ARMOR_IDS.contains(itemId)) { + return false; + } + } + return true; + } + + public static class CooldownEntry { + private final int cooldown; + private final long startTime; + + public CooldownEntry(int cooldown) { + this.cooldown = cooldown; + this.startTime = System.currentTimeMillis(); + } + + public boolean isOnCooldown() { + return (this.startTime + this.cooldown) > System.currentTimeMillis(); + } + + public long getRemainingCooldown() { + long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); + return time <= 0 ? 0 : time; + } + + public float getRemainingCooldownPercent() { + return this.isOnCooldown() ? ((float) this.getRemainingCooldown()) / ((float) cooldown) : 0.0f; + } + } +} -- cgit From ddceeb2734012f7cd6ed7a7df48cf6e857708788 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:13:24 -0400 Subject: Refactor ItemCooldowns --- .../events/ClientPlayerBlockBreakEvent.java | 14 ++++---- .../xmrvizzy/skyblocker/events/SkyblockEvents.java | 33 ++++++++++++++++++ .../mixin/ClientPlayerInteractionManagerMixin.java | 9 +++-- .../skyblocker/mixin/DrawContextMixin.java | 6 +--- .../xmrvizzy/skyblocker/mixin/InGameHudMixin.java | 6 ++-- .../skyblocker/skyblock/item/CustomArmorTrims.java | 2 +- .../skyblocker/skyblock/item/ItemCooldowns.java | 40 +++++++++------------- .../xmrvizzy/skyblocker/utils/SkyblockEvents.java | 33 ------------------ .../java/me/xmrvizzy/skyblocker/utils/Utils.java | 1 + .../utils/discord/DiscordRPCManager.java | 12 +++---- 10 files changed, 76 insertions(+), 80 deletions(-) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/SkyblockEvents.java diff --git a/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java index 9d9463f7..76298612 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java +++ b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java @@ -2,20 +2,22 @@ package me.xmrvizzy.skyblocker.events; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; // Fabric API currently doesn't have an event for this public class ClientPlayerBlockBreakEvent { public static final Event AFTER = EventFactory.createArrayBacked(AfterBlockBreak.class, - (listeners) -> (pos, player) -> { - for (AfterBlockBreak listener : listeners) { - listener.afterBlockBreak(pos, player); - } - }); + (listeners) -> (world, player, pos, state) -> { + for (AfterBlockBreak listener : listeners) { + listener.afterBlockBreak(world, player, pos, state); + } + }); @FunctionalInterface public interface AfterBlockBreak { - void afterBlockBreak(BlockPos pos, PlayerEntity player); + void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java b/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java new file mode 100644 index 00000000..477d68b0 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java @@ -0,0 +1,33 @@ +package me.xmrvizzy.skyblocker.events; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; + +@Environment(EnvType.CLIENT) +public final class SkyblockEvents { + public static final Event JOIN = EventFactory.createArrayBacked(SkyblockEvents.SkyblockJoin.class, callbacks -> () -> { + for (SkyblockEvents.SkyblockJoin callback : callbacks) { + callback.onSkyblockJoin(); + } + }); + + public static final Event LEAVE = EventFactory.createArrayBacked(SkyblockEvents.SkyblockLeave.class, callbacks -> () -> { + for (SkyblockEvents.SkyblockLeave callback : callbacks) { + callback.onSkyblockLeave(); + } + }); + + @Environment(EnvType.CLIENT) + @FunctionalInterface + public interface SkyblockJoin { + void onSkyblockJoin(); + } + + @Environment(EnvType.CLIENT) + @FunctionalInterface + public interface SkyblockLeave { + void onSkyblockLeave(); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java index 4b343317..3963f9d3 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java @@ -1,15 +1,18 @@ package me.xmrvizzy.skyblocker.mixin; import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; +import net.minecraft.block.BlockState; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerInteractionManager; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; 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.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(ClientPlayerInteractionManager.class) public class ClientPlayerInteractionManagerMixin { @@ -17,8 +20,8 @@ public class ClientPlayerInteractionManagerMixin { @Final private MinecraftClient client; - @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V")) - private void skyblocker$blockBreak(BlockPos pos, CallbackInfoReturnable cir) { - ClientPlayerBlockBreakEvent.AFTER.invoker().afterBlockBreak(pos, this.client.player); + @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V"), locals = LocalCapture.CAPTURE_FAILHARD) + private void skyblocker$onBlockBroken(BlockPos pos, CallbackInfoReturnable cir, World world, BlockState blockState) { + ClientPlayerBlockBreakEvent.AFTER.invoker().afterBlockBreak(world, this.client.player, pos, blockState); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java index 7a055488..81c6bdb4 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java @@ -134,10 +134,6 @@ public abstract class DrawContextMixin { @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local ItemStack stack) { - if (Utils.isOnSkyblock() && ItemCooldowns.isItemOnCooldown(stack)) { - return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); - } - - return cooldownProgress; + return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java index 566e6054..7df38bde 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java @@ -78,9 +78,9 @@ public abstract class InGameHudMixin { @ModifyExpressionValue(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getAttackCooldownProgress(F)F")) private float skyblocker$modifyAttackIndicatorCooldown(float cooldownProgress) { - if (Utils.isOnSkyblock()) { - ItemStack stack = this.client.player.getMainHandStack(); - if (ItemCooldowns.isItemOnCooldown(stack)) { + if (Utils.isOnSkyblock() && client.player != null) { + ItemStack stack = client.player.getMainHandStack(); + if (ItemCooldowns.isOnCooldown(stack)) { return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java index dba066a5..6eb0623c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java @@ -8,7 +8,7 @@ import dev.isxander.yacl3.config.v2.api.SerialEntry; import it.unimi.dsi.fastutil.Pair; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SkyblockEvents; +import me.xmrvizzy.skyblocker.events.SkyblockEvents; import me.xmrvizzy.skyblocker.utils.Utils; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java index 232797d6..89e525d4 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java @@ -6,6 +6,7 @@ import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; import me.xmrvizzy.skyblocker.utils.ItemUtils; import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.Hand; @@ -22,28 +23,26 @@ public class ItemCooldowns { private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; private static final ImmutableList BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); + private static final SkyblockerConfig.ItemCooldown config = SkyblockerConfigManager.get().general.itemCooldown; private static final Map itemCooldowns = new HashMap<>(); - private static SkyblockerConfig.ItemCooldown config; public static void init() { - config = SkyblockerConfigManager.get().general.itemCooldown; ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); } - public static void afterBlockBreak(BlockPos pos, PlayerEntity player) { + public static void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state) { if (!config.enableItemCooldowns) return; String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); if (usedItemId == null) return; if (usedItemId.equals(JUNGLE_AXE_ID)) { - if (!isItemOnCooldown(JUNGLE_AXE_ID)) { + if (!isOnCooldown(JUNGLE_AXE_ID)) { itemCooldowns.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); } - } - else if (usedItemId.equals(TREECAPITATOR_ID)) { - if (!isItemOnCooldown(TREECAPITATOR_ID)) { + } else if (usedItemId.equals(TREECAPITATOR_ID)) { + if (!isOnCooldown(TREECAPITATOR_ID)) { itemCooldowns.put(TREECAPITATOR_ID, new CooldownEntry(2000)); } } @@ -54,7 +53,7 @@ public class ItemCooldowns { String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { - if (!isItemOnCooldown(GRAPPLING_HOOK_ID) && !isPlayerWearingBatArmor(player)) { + if (!isOnCooldown(GRAPPLING_HOOK_ID) && !isWearingBatArmor(player)) { itemCooldowns.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); } } @@ -62,18 +61,17 @@ public class ItemCooldowns { return TypedActionResult.pass(ItemStack.EMPTY); } - public static boolean isItemOnCooldown(ItemStack itemStack) { - return isItemOnCooldown(ItemUtils.getItemId(itemStack)); + public static boolean isOnCooldown(ItemStack itemStack) { + return isOnCooldown(ItemUtils.getItemId(itemStack)); } - private static boolean isItemOnCooldown(String itemId) { + private static boolean isOnCooldown(String itemId) { if (itemCooldowns.containsKey(itemId)) { CooldownEntry cooldownEntry = itemCooldowns.get(itemId); if (cooldownEntry.isOnCooldown()) { return true; - } - else { - itemCooldowns.remove(cooldownEntry); + } else { + itemCooldowns.remove(itemId); return false; } } @@ -85,7 +83,7 @@ public class ItemCooldowns { return itemCooldowns.get(ItemUtils.getItemId(itemStack)); } - private static boolean isPlayerWearingBatArmor(PlayerEntity player) { + private static boolean isWearingBatArmor(PlayerEntity player) { for (ItemStack stack : player.getArmorItems()) { String itemId = ItemUtils.getItemId(stack); if (!BAT_ARMOR_IDS.contains(itemId)) { @@ -95,13 +93,9 @@ public class ItemCooldowns { return true; } - public static class CooldownEntry { - private final int cooldown; - private final long startTime; - + public record CooldownEntry(int cooldown, long startTime) { public CooldownEntry(int cooldown) { - this.cooldown = cooldown; - this.startTime = System.currentTimeMillis(); + this(cooldown, System.currentTimeMillis()); } public boolean isOnCooldown() { @@ -110,11 +104,11 @@ public class ItemCooldowns { public long getRemainingCooldown() { long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); - return time <= 0 ? 0 : time; + return Math.max(time, 0); } public float getRemainingCooldownPercent() { - return this.isOnCooldown() ? ((float) this.getRemainingCooldown()) / ((float) cooldown) : 0.0f; + return this.isOnCooldown() ? (float) this.getRemainingCooldown() / cooldown : 0.0f; } } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/SkyblockEvents.java b/src/main/java/me/xmrvizzy/skyblocker/utils/SkyblockEvents.java deleted file mode 100644 index 2dd83ffa..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/SkyblockEvents.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; - -@Environment(EnvType.CLIENT) -public final class SkyblockEvents { - public static final Event JOIN = EventFactory.createArrayBacked(SkyblockEvents.SkyblockJoin.class, callbacks -> () -> { - for (SkyblockEvents.SkyblockJoin callback : callbacks) { - callback.onSkyblockJoin(); - } - }); - - public static final Event LEAVE = EventFactory.createArrayBacked(SkyblockEvents.SkyblockLeave.class, callbacks -> () -> { - for (SkyblockEvents.SkyblockLeave callback : callbacks) { - callback.onSkyblockLeave(); - } - }); - - @Environment(EnvType.CLIENT) - @FunctionalInterface - public interface SkyblockJoin { - void onSkyblockJoin(); - } - - @Environment(EnvType.CLIENT) - @FunctionalInterface - public interface SkyblockLeave { - void onSkyblockLeave(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java index b0fb6edf..20edfca2 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java @@ -2,6 +2,7 @@ package me.xmrvizzy.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import me.xmrvizzy.skyblocker.events.SkyblockEvents; import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; import me.xmrvizzy.skyblocker.skyblock.rift.TheRift; import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java b/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java index 39d91bba..6196fec0 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java @@ -2,7 +2,7 @@ package me.xmrvizzy.skyblocker.utils.discord; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SkyblockEvents; +import me.xmrvizzy.skyblocker.events.SkyblockEvents; import me.xmrvizzy.skyblocker.utils.Utils; import meteordevelopment.discordipc.DiscordIPC; import meteordevelopment.discordipc.RichPresence; @@ -34,7 +34,7 @@ public class DiscordRPCManager { } /** - * Checks the {@link SkyblockerConfigManager.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. + * Checks the {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. */ public static void updateDataAndPresence() { // If the custom message is empty, discord will keep the last message, this is can serve as a default if the user doesn't want a custom message @@ -58,16 +58,16 @@ public class DiscordRPCManager { *

* When the {@link #updateTask previous update} does not exist or {@link CompletableFuture#isDone() has completed}: *

- * Connects to discord if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is enabled}, + * Connects to discord if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}, * the player {@link Utils#isOnSkyblock() is on Skyblock}, and {@link DiscordIPC#isConnected() discord is not already connected}. - * Updates the presence if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is enabled} + * Updates the presence if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled} * and the player {@link Utils#isOnSkyblock() is on Skyblock}. - * Stops the connection if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is disabled} + * Stops the connection if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled} * or the player {@link Utils#isOnSkyblock() is not on Skyblock} and {@link DiscordIPC#isConnected() discord is connected}. * Saves the update task in {@link #updateTask} * * @param initialization whether this is the first time the presence is being updates. If {@code true}, a message will be logged - * if {@link SkyblockerConfigManager.RichPresence#enableRichPresence rich presence is disabled}. + * if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}. */ private static void initAndUpdatePresence(boolean initialization) { if (updateTask == null || updateTask.isDone()) { -- cgit From 9d0f7c22a136544a334f86793e46af8a4f2a3b04 Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:37:21 +0200 Subject: Remove config variable --- .../skyblocker/skyblock/item/ItemCooldowns.java | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java index 89e525d4..7aaf3159 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java @@ -1,7 +1,6 @@ package me.xmrvizzy.skyblocker.skyblock.item; import com.google.common.collect.ImmutableList; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; import me.xmrvizzy.skyblocker.utils.ItemUtils; @@ -23,8 +22,7 @@ public class ItemCooldowns { private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; private static final ImmutableList BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); - private static final SkyblockerConfig.ItemCooldown config = SkyblockerConfigManager.get().general.itemCooldown; - private static final Map itemCooldowns = new HashMap<>(); + private static final Map ITEM_COOLDOWNS = new HashMap<>(); public static void init() { ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); @@ -32,29 +30,29 @@ public class ItemCooldowns { } public static void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state) { - if (!config.enableItemCooldowns) return; + if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return; String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); if (usedItemId == null) return; if (usedItemId.equals(JUNGLE_AXE_ID)) { if (!isOnCooldown(JUNGLE_AXE_ID)) { - itemCooldowns.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); + ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); } } else if (usedItemId.equals(TREECAPITATOR_ID)) { if (!isOnCooldown(TREECAPITATOR_ID)) { - itemCooldowns.put(TREECAPITATOR_ID, new CooldownEntry(2000)); + ITEM_COOLDOWNS.put(TREECAPITATOR_ID, new CooldownEntry(2000)); } } } private static TypedActionResult onItemInteract(PlayerEntity player, World world, Hand hand) { - if (!config.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); + if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { if (!isOnCooldown(GRAPPLING_HOOK_ID) && !isWearingBatArmor(player)) { - itemCooldowns.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); + ITEM_COOLDOWNS.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); } } @@ -66,12 +64,12 @@ public class ItemCooldowns { } private static boolean isOnCooldown(String itemId) { - if (itemCooldowns.containsKey(itemId)) { - CooldownEntry cooldownEntry = itemCooldowns.get(itemId); + if (ITEM_COOLDOWNS.containsKey(itemId)) { + CooldownEntry cooldownEntry = ITEM_COOLDOWNS.get(itemId); if (cooldownEntry.isOnCooldown()) { return true; } else { - itemCooldowns.remove(itemId); + ITEM_COOLDOWNS.remove(itemId); return false; } } @@ -80,7 +78,7 @@ public class ItemCooldowns { } public static CooldownEntry getItemCooldownEntry(ItemStack itemStack) { - return itemCooldowns.get(ItemUtils.getItemId(itemStack)); + return ITEM_COOLDOWNS.get(ItemUtils.getItemId(itemStack)); } private static boolean isWearingBatArmor(PlayerEntity player) { -- cgit From 21d6e2edb48ea7aa88287a5595c5a42511c9785f Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Fri, 6 Oct 2023 00:43:47 -0400 Subject: Item Rarity Backgrounds --- .../java/me/xmrvizzy/skyblocker/SkyblockerMod.java | 1 + .../skyblocker/config/SkyblockerConfig.java | 3 + .../config/categories/GeneralCategory.java | 8 ++ .../skyblocker/mixin/HandledScreenMixin.java | 6 ++ .../xmrvizzy/skyblocker/mixin/InGameHudMixin.java | 9 ++- .../skyblock/item/ItemRarityBackgrounds.java | 86 +++++++++++++++++++++ .../skyblock/item/SkyblockItemRarity.java | 28 +++++++ .../resources/assets/skyblocker/lang/en_us.json | 2 + .../gui/sprites/item_rarity_background.png | Bin 0 -> 198 bytes 9 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java create mode 100644 src/main/resources/assets/skyblocker/textures/gui/sprites/item_rarity_background.png diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java index 07a5be76..cda0b823 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java @@ -101,6 +101,7 @@ public class SkyblockerMod implements ClientModInitializer { QuiverWarning.init(); SpecialEffects.init(); ItemProtection.init(); + ItemRarityBackgrounds.init(); containerSolverManager.init(); statusBarTracker.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java index e40c3f21..17967c53 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -461,6 +461,9 @@ public class SkyblockerConfig { public static class ItemInfoDisplay { @SerialEntry public boolean attributeShardInfo = true; + + @SerialEntry + public boolean itemRarityBackgrounds = false; } public static class SpecialEffects { diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java index c5e2d34c..cd0fe65c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java @@ -348,6 +348,14 @@ public class GeneralCategory { newValue -> config.general.itemInfoDisplay.attributeShardInfo = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip"))) + .binding(defaults.general.itemInfoDisplay.itemRarityBackgrounds, + () -> config.general.itemInfoDisplay.itemRarityBackgrounds, + newValue -> config.general.itemInfoDisplay.itemRarityBackgrounds = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) .build()) //Special Effects diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java index 9ba2107b..7e94d660 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java @@ -9,6 +9,7 @@ import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver; import me.xmrvizzy.skyblocker.skyblock.item.BackpackPreview; import me.xmrvizzy.skyblocker.skyblock.item.CompactorDeletorPreview; import me.xmrvizzy.skyblocker.skyblock.item.ItemProtection; +import me.xmrvizzy.skyblocker.skyblock.item.ItemRarityBackgrounds; import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup; import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; import me.xmrvizzy.skyblocker.utils.Utils; @@ -184,4 +185,9 @@ public abstract class HandledScreenMixin extends Screen private static boolean skyblocker$doesLoreContain(ItemStack stack, MinecraftClient client, String searchString) { return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).anyMatch(line -> line.contains(searchString)); } + + @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) + private void skyblocker$drawItemRarityBackground(DrawContext context, Slot slot, CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(slot.getStack(), context, slot.x, slot.y); + } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java index c73ca2aa..0425b0b8 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java @@ -6,11 +6,13 @@ import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars; import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap; +import me.xmrvizzy.skyblocker.skyblock.item.ItemRarityBackgrounds; import me.xmrvizzy.skyblocker.utils.Utils; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.Identifier; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -33,9 +35,10 @@ public abstract class InGameHudMixin { private int scaledWidth; @Inject(method = "renderHotbar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHotbarItem(Lnet/minecraft/client/gui/DrawContext;IIFLnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;I)V", ordinal = 0)) - public void skyblocker$renderHotbarItemLock(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y) { - if (Utils.isOnSkyblock() && HotbarSlotLock.isLocked(index)) { - context.drawTexture(SLOT_LOCK, x, y, 0, 0, 16, 16); + public void skyblocker$renderHotbarItemLockOrRarityBg(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y, @Local PlayerEntity player) { + if (Utils.isOnSkyblock()) { + if (SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(player.getInventory().main.get(index), context, x, y); + if (HotbarSlotLock.isLocked(index)) context.drawTexture(SLOT_LOCK, x, y, 0, 0, 16, 16); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java new file mode 100644 index 00000000..b6020e6e --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -0,0 +1,86 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableMap; +import com.mojang.blaze3d.systems.RenderSystem; + +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import me.xmrvizzy.skyblocker.SkyblockerMod; +import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.texture.Sprite; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class ItemRarityBackgrounds { + private static final Identifier RARITY_BG_TEX = new Identifier(SkyblockerMod.NAMESPACE, "item_rarity_background"); + private static final Supplier SPRITE = () -> MinecraftClient.getInstance().getGuiAtlasManager().getSprite(RARITY_BG_TEX); + private static final ImmutableMap LORE_RARITIES = ImmutableMap.ofEntries( + Map.entry("ADMIN", SkyblockItemRarity.ADMIN), + Map.entry("SPECIAL", SkyblockItemRarity.SPECIAL), //Very special is the same color so this will cover it + Map.entry("DIVINE", SkyblockItemRarity.DIVINE), + Map.entry("MYTHIC", SkyblockItemRarity.MYTHIC), + Map.entry("LEGENDARY", SkyblockItemRarity.LEGENDARY), + Map.entry("LEGENJERRY", SkyblockItemRarity.LEGENDARY), + Map.entry("EPIC", SkyblockItemRarity.EPIC), + Map.entry("RARE", SkyblockItemRarity.RARE), + Map.entry("UNCOMMON", SkyblockItemRarity.UNCOMMON), + Map.entry("COMMON", SkyblockItemRarity.COMMON)); + + private static final Int2ReferenceOpenHashMap CACHE = new Int2ReferenceOpenHashMap<>(); + + public static void init() { + //Clear the cache every 5 minutes, ints are very compact! + Scheduler.INSTANCE.scheduleCyclic(() -> CACHE.clear(), 4800); + } + + public static void tryDraw(ItemStack stack, DrawContext context, int x, int y) { + MinecraftClient client = MinecraftClient.getInstance(); + + if (client.player != null) { + SkyblockItemRarity itemRarity = getItemRarity(stack, client.player); + + if (itemRarity != null) draw(context, x, y, itemRarity); + } + } + + private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { + if (stack == null || stack.isEmpty()) return null; + + int hashCode = System.identityHashCode(stack); + if (CACHE.containsKey(hashCode)) return CACHE.get(hashCode); + + List tooltip = stack.getTooltip(player, TooltipContext.BASIC); + String[] stringifiedTooltip = tooltip.stream().map(Text::getString).toArray(String[]::new); + + for (String rarity : LORE_RARITIES.keySet()) { + if (Arrays.stream(stringifiedTooltip).anyMatch(line -> line.contains(rarity))) { + SkyblockItemRarity foundRarity = LORE_RARITIES.get(rarity); + + CACHE.put(hashCode, foundRarity); + return LORE_RARITIES.get(rarity); + } + } + + CACHE.put(hashCode, null); + return null; + } + + private static void draw(DrawContext context, int x, int y, SkyblockItemRarity rarity) { + //Enable blending to handle HUD translucency + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + context.drawSprite(x, y, 0, 16, 16, SPRITE.get(), rarity.r, rarity.g, rarity.b, 1f); + + RenderSystem.disableBlend(); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java new file mode 100644 index 00000000..da722196 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java @@ -0,0 +1,28 @@ +package me.xmrvizzy.skyblocker.skyblock.item; + +import net.minecraft.util.Formatting; + +public enum SkyblockItemRarity { + ADMIN(Formatting.DARK_RED), + VERY_SPECIAL(Formatting.RED), + SPECIAL(Formatting.RED), + DIVINE(Formatting.AQUA), + MYTHIC(Formatting.LIGHT_PURPLE), + LEGENDARY(Formatting.GOLD), + EPIC(Formatting.DARK_PURPLE), + RARE(Formatting.BLUE), + UNCOMMON(Formatting.GREEN), + COMMON(Formatting.WHITE); + + public final float r; + public final float g; + public final float b; + + private SkyblockItemRarity(Formatting formatting) { + int rgb = formatting.getColorValue(); + + this.r = ((rgb >> 16) & 0xFF) / 255f; + this.g = ((rgb >> 8) & 0xFF) / 255f; + this.b = (rgb & 0xFF) / 255f; + } +} diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 76b6e768..053e31d2 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -76,6 +76,8 @@ "text.autoconfig.skyblocker.option.general.itemInfoDisplay": "Item Info Display", "text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo": "Attribute Shard Info", "text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo.@Tooltip": "Displays the attribute's level as the stack count and the initials of the attribute's name.", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds": "Item Rarity Backgrounds", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip": "Displays a colored background behind an item, the color represents the item's rarity.", "text.autoconfig.skyblocker.option.general.specialEffects": "Special Effects", "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects": "Rare Dungeon Drop Effects", "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects.@Tooltip": "Adds a special visual effect triggered upon obtaining rare dungeon loot!", diff --git a/src/main/resources/assets/skyblocker/textures/gui/sprites/item_rarity_background.png b/src/main/resources/assets/skyblocker/textures/gui/sprites/item_rarity_background.png new file mode 100644 index 00000000..fd8e8604 Binary files /dev/null and b/src/main/resources/assets/skyblocker/textures/gui/sprites/item_rarity_background.png differ -- cgit From 2aac7dfaa71b5c327f71eecd8b2beaedc6821ecf Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:59:15 +0200 Subject: Add translation string --- src/main/resources/assets/skyblocker/lang/en_us.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 11a14d95..fff1bd3e 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -38,6 +38,8 @@ "text.autoconfig.skyblocker.option.general.fairySouls.highlightFoundSouls": "Highlight found fairy souls", "text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls": "Only highlight nearby fairy souls", "text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls.@Tooltip": "When enabled only fairy souls in a radius of 50 blocks are highlighted", + "text.autoconfig.skyblocker.option.general.itemCooldown": "Item Cooldown", + "text.autoconfig.skyblocker.option.general.itemCooldown.enableItemCooldowns": "Enable Item Cooldown", "text.autoconfig.skyblocker.option.general.shortcuts": "Shortcuts", "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts": "Enable Shortcuts", "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "Only works on Hypixel. Edit shortcuts with \"/skyblocker shortcuts\". At least one of the following options must be enabled for this to take effect.", -- cgit From 19c8450f00b1f18725deb8014b642817bb607682 Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Fri, 6 Oct 2023 17:12:56 +0200 Subject: Add check if the broken block is wooden --- .../skyblocker/skyblock/item/ItemCooldowns.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java index 7aaf3159..35b520a5 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java @@ -8,6 +8,7 @@ import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.sound.BlockSoundGroup; import net.minecraft.util.Hand; import net.minecraft.util.TypedActionResult; import net.minecraft.util.math.BlockPos; @@ -35,13 +36,15 @@ public class ItemCooldowns { String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); if (usedItemId == null) return; - if (usedItemId.equals(JUNGLE_AXE_ID)) { - if (!isOnCooldown(JUNGLE_AXE_ID)) { - ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); - } - } else if (usedItemId.equals(TREECAPITATOR_ID)) { - if (!isOnCooldown(TREECAPITATOR_ID)) { - ITEM_COOLDOWNS.put(TREECAPITATOR_ID, new CooldownEntry(2000)); + if (state.getSoundGroup() == BlockSoundGroup.WOOD && state.isBurnable()) { + if (usedItemId.equals(JUNGLE_AXE_ID)) { + if (!isOnCooldown(JUNGLE_AXE_ID)) { + ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); + } + } else if (usedItemId.equals(TREECAPITATOR_ID)) { + if (!isOnCooldown(TREECAPITATOR_ID)) { + ITEM_COOLDOWNS.put(TREECAPITATOR_ID, new CooldownEntry(2000)); + } } } } -- cgit From 05d86bf32ef2d46a695c376609d4633011de029a Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sat, 7 Oct 2023 04:36:44 -0400 Subject: Performance Optimizations --- .../skyblock/item/ItemRarityBackgrounds.java | 25 +++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java index b6020e6e..87dac046 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -10,19 +10,23 @@ import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; import me.xmrvizzy.skyblocker.SkyblockerMod; +import me.xmrvizzy.skyblocker.utils.Utils; import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawContext; import net.minecraft.client.item.TooltipContext; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.texture.Sprite; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; import net.minecraft.text.Text; import net.minecraft.util.Identifier; public class ItemRarityBackgrounds { private static final Identifier RARITY_BG_TEX = new Identifier(SkyblockerMod.NAMESPACE, "item_rarity_background"); private static final Supplier SPRITE = () -> MinecraftClient.getInstance().getGuiAtlasManager().getSprite(RARITY_BG_TEX); + private static final String EMPTY = ""; private static final ImmutableMap LORE_RARITIES = ImmutableMap.ofEntries( Map.entry("ADMIN", SkyblockItemRarity.ADMIN), Map.entry("SPECIAL", SkyblockItemRarity.SPECIAL), //Very special is the same color so this will cover it @@ -40,6 +44,15 @@ public class ItemRarityBackgrounds { public static void init() { //Clear the cache every 5 minutes, ints are very compact! Scheduler.INSTANCE.scheduleCyclic(() -> CACHE.clear(), 4800); + + //Clear cache after a screen where items can be upgraded in rarity closes + ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + String title = screen.getTitle().getString(); + + if (Utils.isOnSkyblock() && (title.equals("The Hex") || title.equals("Craft Item") || title.equals("Anvil") || title.equals("Reforge Anvil"))) { + ScreenEvents.remove(screen).register(screen1 -> CACHE.clear()); + } + }); } public static void tryDraw(ItemStack stack, DrawContext context, int x, int y) { @@ -55,7 +68,17 @@ public class ItemRarityBackgrounds { private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { if (stack == null || stack.isEmpty()) return null; - int hashCode = System.identityHashCode(stack); + int hashCode = 0; + NbtCompound nbt = stack.getNbt(); + + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : EMPTY; + + //If the item has a uuid, then use the hash code of the uuid otherwise use the identity hash code of the stack + hashCode = (itemUuid != EMPTY) ? itemUuid.hashCode() : System.identityHashCode(stack); + } + if (CACHE.containsKey(hashCode)) return CACHE.get(hashCode); List tooltip = stack.getTooltip(player, TooltipContext.BASIC); -- cgit From 95faf143b98d5d5edc3c711e4d22e5f2115119a7 Mon Sep 17 00:00:00 2001 From: alexia Date: Sat, 7 Oct 2023 00:59:15 +0200 Subject: Hook actual durability for Pickonimbus/Drills Instead of the previous DrawContext hack. Confirmed working with Durability Notifier on Fabric 1.20.2. --- .../skyblocker/mixin/DrawContextMixin.java | 66 ---------------------- .../me/xmrvizzy/skyblocker/mixin/ItemMixin.java | 30 ++++++++++ .../xmrvizzy/skyblocker/mixin/ItemStackMixin.java | 29 ++++++++++ .../me/xmrvizzy/skyblocker/utils/ItemUtils.java | 54 ++++++++++++++++++ src/main/resources/skyblocker.mixins.json | 1 + 5 files changed, 114 insertions(+), 66 deletions(-) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java index 356095ab..2cb83e87 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java @@ -2,7 +2,6 @@ package me.xmrvizzy.skyblocker.mixin; import com.llamalad7.mixinextras.sugar.Local; import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import com.mojang.blaze3d.systems.RenderSystem; import dev.cbyrne.betterinject.annotations.Arg; import dev.cbyrne.betterinject.annotations.Inject; @@ -12,12 +11,10 @@ import me.xmrvizzy.skyblocker.utils.ItemUtils; import me.xmrvizzy.skyblocker.utils.Utils; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.util.Formatting; -import net.minecraft.util.math.ColorHelper; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Final; @@ -25,78 +22,15 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import java.awt.Color; -import java.util.regex.Pattern; - @Mixin(DrawContext.class) public abstract class DrawContextMixin { @Shadow @Final private MatrixStack matrices; - @Shadow - public abstract void fill(RenderLayer layer, int x1, int x2, int y1, int y2, int color); - @Shadow public abstract int drawText(TextRenderer textRenderer, @Nullable String text, int x, int y, int color, boolean shadow); - @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD")) - public void skyblocker$renderItemBar(@Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y) { - if (!Utils.isOnSkyblock() || !SkyblockerConfigManager.get().locations.dwarvenMines.enableDrillFuel || stack.isEmpty()) { - return; - } - - NbtCompound tag = stack.getNbt(); - if (tag == null || !tag.contains("ExtraAttributes")) { - return; - } - - NbtCompound extraAttributes = tag.getCompound("ExtraAttributes"); - if (!extraAttributes.contains("drill_fuel") && !extraAttributes.getString("id").equals("PICKONIMBUS")) { - return; - } - matrices.push(); - matrices.translate(0f, 0f, 200f); - RenderSystem.disableDepthTest(); - - float current = 0.0F; - float max = 0.0F; - String clearFormatting = ""; - - for (String line : ItemUtils.getTooltipStrings(stack)) { - clearFormatting = Formatting.strip(line); - if (line.contains("Fuel: ")) { - if (clearFormatting != null) { - String clear = Pattern.compile("[^0-9 /]").matcher(clearFormatting).replaceAll("").trim(); - String[] split = clear.split("/"); - current = Integer.parseInt(split[0]); - max = Integer.parseInt(split[1]) * 1000; - } - break; - } else if (line.contains("uses.")) { - if (clearFormatting != null) { - int startIndex = clearFormatting.lastIndexOf("after") + 6; - int endIndex = clearFormatting.indexOf("uses", startIndex); - if (startIndex >= 0 && endIndex > startIndex) { - String usesString = clearFormatting.substring(startIndex, endIndex).trim(); - current = Integer.parseInt(usesString); - max = 5000; - } - } - break; - } - } - - float hue = Math.max(0.0F, 1.0F - (max - current) / max); - int width = Math.round(current / max * 13.0F); - Color color = Color.getHSBColor(hue / 3.0F, 1.0F, 1.0F); - this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 15, y + 15, 0xFF000000); - this.fill(RenderLayer.getGuiOverlay(), x + 2, y + 13, x + 2 + width, y + 14, ColorHelper.Argb.getArgb(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue())); - - matrices.pop(); - RenderSystem.enableDepthTest(); - } - @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD")) private void skyblocker$renderAttributeShardDisplay(@Arg TextRenderer textRenderer, @Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y, @Local(argsOnly = true) LocalRef countOverride) { if (!SkyblockerConfigManager.get().general.itemInfoDisplay.attributeShardInfo) return; diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java new file mode 100644 index 00000000..fdede686 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java @@ -0,0 +1,30 @@ +package me.xmrvizzy.skyblocker.mixin; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +@Mixin(Item.class) +public abstract class ItemMixin { + @WrapOperation( + method = "getItemBarColor", + at = @At(value = "FIELD", target = "Lnet/minecraft/item/Item;maxDamage:I", opcode = Opcodes.GETFIELD) + ) + private int skyblocker$handlePickoDrillBarColor(Item item, Operation original, ItemStack stack) { + return stack.getMaxDamage(); + } + + @WrapOperation( + method = "getItemBarStep", + at = @At(value = "FIELD", target = "Lnet/minecraft/item/Item;maxDamage:I", opcode = Opcodes.GETFIELD) + ) + private int skyblocker$handlePickoDrillBarStep(Item item, Operation original, ItemStack stack) { + return stack.getMaxDamage(); + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java index 063c048e..b235956a 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java @@ -8,6 +8,8 @@ import org.spongepowered.asm.mixin.injection.At; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.ItemUtils; +import me.xmrvizzy.skyblocker.utils.ItemUtils.Durability; import me.xmrvizzy.skyblocker.utils.Utils; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; @@ -30,4 +32,31 @@ public abstract class ItemStackMixin { return original; } + + @ModifyReturnValue(method = "getDamage", at = @At("RETURN")) + private int skyblocker$handlePickoDrillDamage(int original) { + Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + if (dur != null) { + return dur.max() - dur.current(); + } + return original; + } + + @ModifyReturnValue(method = "getMaxDamage", at = @At("RETURN")) + private int skyblocker$handlePickoDrillMaxDamage(int original) { + Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + if (dur != null) { + return dur.max(); + } + return original; + } + + @ModifyReturnValue(method = "isDamageable", at = @At("RETURN")) + private boolean skyblocker$handlePickoDrillDamageable(boolean original) { + Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + if (dur != null) { + return true; + } + return original; + } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java index 5c12b777..0be4d403 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java @@ -1,11 +1,15 @@ package me.xmrvizzy.skyblocker.utils; import com.mojang.brigadier.exceptions.CommandSyntaxException; +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import net.minecraft.client.MinecraftClient; import net.minecraft.client.item.TooltipContext; import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.StringNbtReader; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; @@ -15,6 +19,56 @@ import java.util.regex.Pattern; public class ItemUtils { private final static Pattern WHITESPACES = Pattern.compile("^\\s*$"); + public record Durability(int current, int max) { + } + + @Nullable + public static Durability getDurability(ItemStack stack) { + if (!Utils.isOnSkyblock() || !SkyblockerConfigManager.get().locations.dwarvenMines.enableDrillFuel || stack.isEmpty()) { + return null; + } + + NbtCompound tag = stack.getNbt(); + if (tag == null || !tag.contains("ExtraAttributes")) { + return null; + } + + NbtCompound extraAttributes = tag.getCompound("ExtraAttributes"); + if (!extraAttributes.contains("drill_fuel") && !extraAttributes.getString("id").equals("PICKONIMBUS")) { + return null; + } + + int current = 0; + int max = 0; + String clearFormatting = ""; + + for (String line : ItemUtils.getTooltipStrings(stack)) { + clearFormatting = Formatting.strip(line); + if (line.contains("Fuel: ")) { + if (clearFormatting != null) { + String clear = Pattern.compile("[^0-9 /]").matcher(clearFormatting).replaceAll("").trim(); + String[] split = clear.split("/"); + current = Integer.parseInt(split[0]); + max = Integer.parseInt(split[1]) * 1000; + return new Durability(current, max); + } + } else if (line.contains("uses.")) { + if (clearFormatting != null) { + int startIndex = clearFormatting.lastIndexOf("after") + 6; + int endIndex = clearFormatting.indexOf("uses", startIndex); + if (startIndex >= 0 && endIndex > startIndex) { + String usesString = clearFormatting.substring(startIndex, endIndex).trim(); + current = Integer.parseInt(usesString); + max = 5000; + } + return new Durability(current, max); + } + } + } + + return null; + } + public static List getTooltip(ItemStack item) { MinecraftClient client = MinecraftClient.getInstance(); return client.player == null || item == null ? Collections.emptyList() : item.getTooltip(client.player, TooltipContext.Default.BASIC); diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index a4fe92dd..05759d84 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -16,6 +16,7 @@ "HandledScreenMixin", "InGameHudMixin", "InventoryScreenMixin", + "ItemMixin", "ItemStackMixin", "LeverBlockMixin", "MinecraftClientMixin", -- cgit From a7c361260a2a104261cf301125b4c81c615334f0 Mon Sep 17 00:00:00 2001 From: alexia Date: Sat, 7 Oct 2023 17:28:14 +0200 Subject: Apply suggestions from code review Co-authored-by: Kevin <92656833+kevinthegreat1@users.noreply.github.com> --- src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java | 6 +++--- src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java index b235956a..280c01ba 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java @@ -34,7 +34,7 @@ public abstract class ItemStackMixin { } @ModifyReturnValue(method = "getDamage", at = @At("RETURN")) - private int skyblocker$handlePickoDrillDamage(int original) { + private int skyblocker$handleDamage(int original) { Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); if (dur != null) { return dur.max() - dur.current(); @@ -43,7 +43,7 @@ public abstract class ItemStackMixin { } @ModifyReturnValue(method = "getMaxDamage", at = @At("RETURN")) - private int skyblocker$handlePickoDrillMaxDamage(int original) { + private int skyblocker$handleMaxDamage(int original) { Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); if (dur != null) { return dur.max(); @@ -52,7 +52,7 @@ public abstract class ItemStackMixin { } @ModifyReturnValue(method = "isDamageable", at = @At("RETURN")) - private boolean skyblocker$handlePickoDrillDamageable(boolean original) { + private boolean skyblocker$handleDamageable(boolean original) { Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); if (dur != null) { return true; diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java index 0be4d403..0d227bc3 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java @@ -40,7 +40,7 @@ public class ItemUtils { int current = 0; int max = 0; - String clearFormatting = ""; + String clearFormatting; for (String line : ItemUtils.getTooltipStrings(stack)) { clearFormatting = Formatting.strip(line); -- cgit From bc22e75e2eac6526cd7dc8ec99f32469ee4cc5d7 Mon Sep 17 00:00:00 2001 From: alexia Date: Sat, 7 Oct 2023 17:29:29 +0200 Subject: Address further review comments --- .../me/xmrvizzy/skyblocker/utils/ItemUtils.java | 38 +++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java index 0d227bc3..30c20524 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java @@ -19,7 +19,22 @@ import java.util.regex.Pattern; public class ItemUtils { private final static Pattern WHITESPACES = Pattern.compile("^\\s*$"); - public record Durability(int current, int max) { + public static List getTooltip(ItemStack item) { + MinecraftClient client = MinecraftClient.getInstance(); + return client.player == null || item == null ? Collections.emptyList() : item.getTooltip(client.player, TooltipContext.Default.BASIC); + } + + public static List getTooltipStrings(ItemStack item) { + List lines = getTooltip(item); + List list = new ArrayList<>(); + + for (Text line : lines) { + String string = line.getString(); + if (!WHITESPACES.matcher(string).matches()) + list.add(string); + } + + return list; } @Nullable @@ -69,24 +84,6 @@ public class ItemUtils { return null; } - public static List getTooltip(ItemStack item) { - MinecraftClient client = MinecraftClient.getInstance(); - return client.player == null || item == null ? Collections.emptyList() : item.getTooltip(client.player, TooltipContext.Default.BASIC); - } - - public static List getTooltipStrings(ItemStack item) { - List lines = getTooltip(item); - List list = new ArrayList<>(); - - for (Text line : lines) { - String string = line.getString(); - if (!WHITESPACES.matcher(string).matches()) - list.add(string); - } - - return list; - } - public static ItemStack getSkyblockerStack() { try { return ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}")); @@ -94,4 +91,7 @@ public class ItemUtils { throw new RuntimeException(e); } } + + public record Durability(int current, int max) { + } } -- cgit From 238ed9ac50ecd4be790b0f30d54e962177fbb8d5 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 7 Oct 2023 11:17:13 -0400 Subject: Refactor ItemRarityBackgrounds --- .../skyblock/item/ItemRarityBackgrounds.java | 27 +++++++++++----------- .../skyblock/item/SkyblockItemRarity.java | 7 +++--- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java index 87dac046..18a88539 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -26,7 +26,6 @@ import net.minecraft.util.Identifier; public class ItemRarityBackgrounds { private static final Identifier RARITY_BG_TEX = new Identifier(SkyblockerMod.NAMESPACE, "item_rarity_background"); private static final Supplier SPRITE = () -> MinecraftClient.getInstance().getGuiAtlasManager().getSprite(RARITY_BG_TEX); - private static final String EMPTY = ""; private static final ImmutableMap LORE_RARITIES = ImmutableMap.ofEntries( Map.entry("ADMIN", SkyblockItemRarity.ADMIN), Map.entry("SPECIAL", SkyblockItemRarity.SPECIAL), //Very special is the same color so this will cover it @@ -37,13 +36,13 @@ public class ItemRarityBackgrounds { Map.entry("EPIC", SkyblockItemRarity.EPIC), Map.entry("RARE", SkyblockItemRarity.RARE), Map.entry("UNCOMMON", SkyblockItemRarity.UNCOMMON), - Map.entry("COMMON", SkyblockItemRarity.COMMON)); - + Map.entry("COMMON", SkyblockItemRarity.COMMON) + ); private static final Int2ReferenceOpenHashMap CACHE = new Int2ReferenceOpenHashMap<>(); public static void init() { //Clear the cache every 5 minutes, ints are very compact! - Scheduler.INSTANCE.scheduleCyclic(() -> CACHE.clear(), 4800); + Scheduler.INSTANCE.scheduleCyclic(CACHE::clear, 4800); //Clear cache after a screen where items can be upgraded in rarity closes ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { @@ -64,7 +63,7 @@ public class ItemRarityBackgrounds { if (itemRarity != null) draw(context, x, y, itemRarity); } } - + private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { if (stack == null || stack.isEmpty()) return null; @@ -73,23 +72,23 @@ public class ItemRarityBackgrounds { if (nbt != null && nbt.contains("ExtraAttributes")) { NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : EMPTY; + String itemUuid = extraAttributes.getString("uuid"); - //If the item has a uuid, then use the hash code of the uuid otherwise use the identity hash code of the stack - hashCode = (itemUuid != EMPTY) ? itemUuid.hashCode() : System.identityHashCode(stack); + //If the item has an uuid, then use the hash code of the uuid otherwise use the identity hash code of the stack + hashCode = itemUuid.isEmpty() ? System.identityHashCode(stack) : itemUuid.hashCode(); } - + if (CACHE.containsKey(hashCode)) return CACHE.get(hashCode); List tooltip = stack.getTooltip(player, TooltipContext.BASIC); String[] stringifiedTooltip = tooltip.stream().map(Text::getString).toArray(String[]::new); - for (String rarity : LORE_RARITIES.keySet()) { - if (Arrays.stream(stringifiedTooltip).anyMatch(line -> line.contains(rarity))) { - SkyblockItemRarity foundRarity = LORE_RARITIES.get(rarity); + for (String rarityString : LORE_RARITIES.keySet()) { + if (Arrays.stream(stringifiedTooltip).anyMatch(line -> line.contains(rarityString))) { + SkyblockItemRarity rarity = LORE_RARITIES.get(rarityString); - CACHE.put(hashCode, foundRarity); - return LORE_RARITIES.get(rarity); + CACHE.put(hashCode, rarity); + return rarity; } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java index da722196..f7ff1fb9 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java @@ -17,10 +17,11 @@ public enum SkyblockItemRarity { public final float r; public final float g; public final float b; - - private SkyblockItemRarity(Formatting formatting) { + + SkyblockItemRarity(Formatting formatting) { + @SuppressWarnings("DataFlowIssue") int rgb = formatting.getColorValue(); - + this.r = ((rgb >> 16) & 0xFF) / 255f; this.g = ((rgb >> 8) & 0xFF) / 255f; this.b = (rgb & 0xFF) / 255f; -- cgit From 0103dcafd33e1cd0a267fe788ec5bc5da0b5c72b Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sat, 7 Oct 2023 15:44:03 -0400 Subject: Add item rarity background opacity --- .../me/xmrvizzy/skyblocker/config/ConfigUtils.java | 4 ++ .../skyblocker/config/SkyblockerConfig.java | 3 ++ .../config/categories/GeneralCategory.java | 48 ++++++++++++---------- .../skyblock/item/ItemRarityBackgrounds.java | 5 ++- .../resources/assets/skyblocker/lang/en_us.json | 1 + 5 files changed, 37 insertions(+), 24 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java index 28ed704b..766b0643 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java @@ -3,9 +3,13 @@ package me.xmrvizzy.skyblocker.config; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; import dev.isxander.yacl3.api.controller.EnumControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; import me.xmrvizzy.skyblocker.config.controllers.EnumDropdownControllerBuilder; +import net.minecraft.text.Text; public class ConfigUtils { + public static final ValueFormatter FLOAT_TWO_FORMATTER = value -> Text.literal(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); + public static BooleanControllerBuilder createBooleanController(Option opt) { return BooleanControllerBuilder.create(opt).yesNoFormatter().coloured(true); } diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java index 17967c53..3cb2ce5d 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -464,6 +464,9 @@ public class SkyblockerConfig { @SerialEntry public boolean itemRarityBackgrounds = false; + + @SerialEntry + public float itemRarityBackgroundsOpacity = 1f; } public static class SpecialEffects { diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java index cd0fe65c..e5b7d92d 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java @@ -1,15 +1,12 @@ package me.xmrvizzy.skyblocker.config.categories; -import dev.isxander.yacl3.api.ButtonOption; -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.*; import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.config.ConfigUtils; +import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.skyblock.shortcut.ShortcutsConfigScreen; import me.xmrvizzy.skyblocker.utils.render.title.TitleContainerConfigScreen; import net.minecraft.client.MinecraftClient; @@ -17,10 +14,10 @@ import net.minecraft.text.Text; public class GeneralCategory { - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { return ConfigCategory.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.category.general")) - + //Ungrouped Options .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.acceptReparty")) @@ -57,7 +54,7 @@ public class GeneralCategory { newValue -> config.general.hideStatusEffectOverlay = newValue) .controller(ConfigUtils::createBooleanController) .build()) - + //Tab Hud .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud")) @@ -94,7 +91,7 @@ public class GeneralCategory { .controller(ConfigUtils::createEnumCyclingListController) .build()) .build()) - + //Fancy Bars .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars")) @@ -135,7 +132,7 @@ public class GeneralCategory { .controller(ConfigUtils::createEnumCyclingListController) .build()) .build()) - + //Experiments Solver .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments")) @@ -162,7 +159,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Fishing Helper .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.fishing")) @@ -175,7 +172,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Fairy Souls Helper .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls")) @@ -203,7 +200,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Shortcuts .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts")) @@ -238,7 +235,7 @@ public class GeneralCategory { .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new ShortcutsConfigScreen(screen))) .build()) .build()) - + //Quiver Warning .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning")) @@ -265,7 +262,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item List .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemList")) @@ -278,7 +275,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item Tooltip .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip")) @@ -335,7 +332,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Item Info Display .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay")) @@ -356,8 +353,15 @@ public class GeneralCategory { newValue -> config.general.itemInfoDisplay.itemRarityBackgrounds = newValue) .controller(ConfigUtils::createBooleanController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundsOpacity")) + .binding(defaults.general.itemInfoDisplay.itemRarityBackgroundsOpacity, + () -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity, + newValue -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity = newValue) + .controller(opt -> FloatSliderControllerBuilder.create(opt).range(0f, 1f).step(0.05f).formatValue(ConfigUtils.FLOAT_TWO_FORMATTER)) + .build()) .build()) - + //Special Effects .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects")) @@ -371,7 +375,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Hitboxes .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox")) @@ -391,7 +395,7 @@ public class GeneralCategory { .controller(ConfigUtils::createBooleanController) .build()) .build()) - + //Title Container .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer")) @@ -438,7 +442,7 @@ public class GeneralCategory { .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new TitleContainerConfigScreen(screen))) .build()) .build()) - + //Teleport Overlays .group(OptionGroup.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay")) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java index 18a88539..837c209a 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -10,6 +10,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; import me.xmrvizzy.skyblocker.SkyblockerMod; +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.utils.Utils; import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; @@ -100,8 +101,8 @@ public class ItemRarityBackgrounds { //Enable blending to handle HUD translucency RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); - - context.drawSprite(x, y, 0, 16, 16, SPRITE.get(), rarity.r, rarity.g, rarity.b, 1f); + + context.drawSprite(x, y, 0, 16, 16, SPRITE.get(), rarity.r, rarity.g, rarity.b, SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgroundsOpacity); RenderSystem.disableBlend(); } diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 053e31d2..17cbde14 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -78,6 +78,7 @@ "text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo.@Tooltip": "Displays the attribute's level as the stack count and the initials of the attribute's name.", "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds": "Item Rarity Backgrounds", "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip": "Displays a colored background behind an item, the color represents the item's rarity.", + "text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundsOpacity": "Item Rarity Backgrounds Opacity", "text.autoconfig.skyblocker.option.general.specialEffects": "Special Effects", "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects": "Rare Dungeon Drop Effects", "text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects.@Tooltip": "Adds a special visual effect triggered upon obtaining rare dungeon loot!", -- cgit From 692059c2ee8bbb3db980059fd5a5819954793009 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sat, 7 Oct 2023 16:41:57 -0400 Subject: Update shortcuts description (#342) --- src/main/resources/assets/skyblocker/lang/en_us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 76b6e768..7905026b 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -40,7 +40,7 @@ "text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls.@Tooltip": "When enabled only fairy souls in a radius of 50 blocks are highlighted", "text.autoconfig.skyblocker.option.general.shortcuts": "Shortcuts", "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts": "Enable Shortcuts", - "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "Only works on Hypixel. Edit shortcuts with \"/skyblocker shortcuts\". At least one of the following options must be enabled for this to take effect.", + "text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip": "Works anywhere, even in vanilla! Edit shortcuts with \"/skyblocker shortcuts\". At least one of the following options must be enabled for this to take effect.", "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts": "Enable Command Shortcuts", "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts.@Tooltip": "Shortcuts for commands consisting of only one word. Edit shortcuts with \"/skyblocker shortcuts\". Shortcuts must be enabled for this to take effect.", "text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts": "Enable Command Argument Shortcuts", -- cgit From 5ca4287e111f110ee77d2b6086ca7c9d962fdfbd Mon Sep 17 00:00:00 2001 From: Yasin Date: Sun, 8 Oct 2023 06:00:39 +0200 Subject: sync MRREADME.md with modrinth description (#338) - with this change the mrreadme is synched with modrinth description everytime the mod gets uploaded to modrinth. - Also added emi to optional list --- MRREADME.md | 225 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ build.gradle | 14 ++-- 2 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 MRREADME.md diff --git a/MRREADME.md b/MRREADME.md new file mode 100644 index 00000000..7e1e68ad --- /dev/null +++ b/MRREADME.md @@ -0,0 +1,225 @@ + + +## Skyblocker + +Hypixel Skyblock Mod for Minecraft 1.17.x + 1.18.x + 1.19.x + 1.20.x + +## 🔧 Configuration - [Mod Menu](https://modrinth.com/mod/modmenu) not required +open config with modmenu \ +or **/skyblocker config** + +Skyblocker has a variety of configurations. \ +To access the configuration menu, you must install [Mod Menu](https://modrinth.com/mod/modmenu). +

Mod Menu
Mod Menu - Skyblocker config
+ +### List of Configuration + +
+ General + +|Config option|Description| +|---|---| +| Update Notification | Get notified when there is a new update | +| View [backpack preview](https://cdn-raw.modrinth.com/data/y6DuFGwJ/images/ef33e34b79c1615bcb23f3a395b29b793ef32e34.png) without holding shift | Preview on hover | + +
+ Health, Mana, Defence & XP Bars + +|Config option|Description| +|---|---| +|Enable Bars| Change Minecraft health ui with skyblocker [custom ui](https://user-images.githubusercontent.com/27798256/170806938-f858f0ae-4d8b-4767-9b53-8fe5a65edf56.png)| +|Configure Bar Position|[Customize Bar Positions](https://cdn.discordapp.com/attachments/1103292463558438993/1103292498345984070/healt-layer.png)| +
+ +
+ Item List + +|Config option|Description| +|---|---| +|Enable Item List| Acitvate [recipe viewer](https://cdn-raw.modrinth.com/data/y6DuFGwJ/images/cf9f8077067b9781686f23116f163d529c21c404.png)| +
+ +
+ Item Tooltip + +Customize [Item tooltip](https://cdn-raw.modrinth.com/data/y6DuFGwJ/images/12903f3f839d769fac48a4e74e04bee9aa1657d5.png) + +
+ +
+ Hitboxes + +|Config option|Description| +|---|---| +|Enable 1.8 farmland hitbox| Change hitbox to the 1.8 one | +|Enable 1.8 lever hitbox| Change hitbox to the 1.8 one | +
+
+ +
+ Locations + +
+ Dungeons + +|Config option|Description| +|---|---| +|Croseus Helper|Gray out chests that have already been opened| +|Enable Map| [Map](https://cdn-raw.modrinth.com/data/y6DuFGwJ/images/43243429b1c4d17236ae3e5a9836ecd7d905644b.png)| +|Solve Three Weirdos Puzzle|Solver usefull in Dungeons| +|Solve Blaze Puzzle|Solver usefull in Dungeons| +|Solve Trivia Puzzle|Solver usefull in Dungeons| + +
+ Terminal + +|Config option|Description| +|---|---| +|Solve Selectect Colored|Solver usefull in Dungeons 7| +|Solve Click In Order|Solver usefull in Dungeons 7| +|Solve Starts With|Solver usefull in Dungeons 7| +
+
+
+ Dwarven Mines + +|Config option|Description| +|---|---| +|Enable Drill Fuel|[Drill icon](https://cdn-raw.modrinth.com/data/y6DuFGwJ/images/43c7ab7aa7c90fcf833c7cddbf73e6644c6ce5fa.png)| +|Solve Fetchur| Solver usefull in Mines| +|Solve Puzzler Puzzle| Solver usefull in Mines | +|Dwarven Hud| [Commision Hud](https://cdn.discordapp.com/attachments/905867886428565565/950513333478494248/Screen_Shot_2022-03-07_at_4.58.12_PM.png)| +
+
+ + +
+ Quick Navigation + +|Config option|Description| +|---|---| +|Enable Quick Navigation|Enable [Quicknav](https://cdn.discordapp.com/attachments/1103292463558438993/1103358576870817866/quick-nav.png)| +
+ Button 1-12 + +|Config option|Description| +|---|---| +|Render|To show the tab| +|Item name| The name of the item e.g. iron_boots | +|NBT| NBT tag of the item e.g. custom head id on skull item| +|UI Title| Title of the tab| +|Click event|The command that is executed when you click the tab| +
+
+ + +
+ Messages + +|Config option|Description| +|---|---| +|Hide Ability Cooldown|Disable,Filter or Move to action bar| +|Hide Heal Messages|Disable,Filter or Move to action bar| +|Hide AOTE Messages|Disable,Filter or Move to action bar| +|Hide Implosion Message|Disable,Filter or Move to action bar| +|Hide Molten Wave Message|Disable,Filter or Move to action bar| +|Hide Ads from Public Chat|Disable,Filter or Move to action bar| +|Hide Teleport Pad Messages|Disable,Filter or Move to action bar| +|Hide Combo Messages|Disable,Filter or Move to action bar| +|Hide Autopet Messages|Disable,Filter or Move to action bar| +|Hide Mana Consumption Messages from Action Bar|Activate or deactivate| +
+ + +
+ Discord Rich Presence + +|Config option|Description| +|---|---| +|Enable|Activate [Discord Rich Presence](https://cdn-raw.modrinth.com/data/y6DuFGwJ/images/f6314d0ae0fc24d77fb3371e59b7abfe4774a17e.png)| +|Skyblock Info|Choose between Location,Purse and Bits| +|Cycle Skyblock Info| Cycles between the three options| +|Custom Message| Show a custom message| +
+ +## 🖼️ Images +Click [#Gallery](https://modrinth.com/mod/skyblocker-liap/gallery) for images to this mod + +## 📖Features +
+open + +* Bars: Health and absorption, Mana, Defense, XP (moving fancy bars) +* Hide Messages: Ability Cooldown, Heal, AOTE, Implosion, Molten Wave, Teleport Pad Messages +* Dungeon Minimap +* Dungeon Secrets +* Starred Mob Glow +* Dungeon Puzzle Solver: + * Three Weirdos + * Blaze + * Tic Tac Toe + * Croesus + * Terminal: + * Order + * Coloured Items + * Item Name +* Dwarven Mines Solver: Fetchur, Puzzler +* Barn Solver: Treasure Hunter, Hungry Hiker +* Experiments Solvers +* Slayer helper: + * Vampire : Effigy Waypoints, Healing Melon Indicator, Twinclaws Ice Indicator, Steak Stake Indicator +* Drill Fuel and Pickonimbus 2000 in Item Durability Bar +* Hotbar Slot Lock Keybind (Select the hotbar slot you want to lock/unlock and press the lockbutton) +* Price tooltip: npc, motes, bazaar (avg, lbin), ah, museum +* reparty: write /rp to reparty + auto rejoin +* Wiki Lookup: press f4 to open the wiki page about the held item +* Discord Rich Presence: Allows user to show either their Piggy, Bits, or location. Along with a custom message +* Quicknav: fast navigate between pets, armor, enderchest, skill, collection, crafting, enchant, envil, warp dungeon, + warp hub +* Recipe book: in the vanilla recipe book all skyblock items are listed, and you can see the recipe of the item +* Backpack preview: after you clicked your backpack or enderchest once you can hover over the backpack or enderchest and + hold shift to preview +* Update notification +* Commission HUD: Dwarven Mines quests +* 1.8 hitbox for lever and farmland +* Custom Tab HUD + Fully configureable with ressourcepack +* Roughly enough items (REI) and Emi Support +* Fishing Helper + sound notification +* Mirrorverse Waypoints +* Attribute Shard Info Display +* Item and Armour customisation: + * Item Renaming + * Custom Armour Dye Colours + * Custom Armour Trims +* OptiFabric Compatibility +* Rare Drop Special Effects! Dungeon chest +* Dungeon Chest Profit Calculator +* Hide Status Effect Overlay +* Personal Compactor/Deletor preview +* Hide fake players in social interactions screen +* hidden relic helper + +
+ + +## [Modpack](https://modrinth.com/modpack/skyblocker-modpack) + +
+open + +1. [Here](https://docs.modrinth.com/docs/modpacks/playing_modpacks) is a tutorial how to install it easy with a launcher. +2. Download the [Skyblocker Modpack](https://modrinth.com/modpack/skyblocker-modpack/version/latest). + * Drag and drop the .mrpack file into a Custom MC Launcher like [MultiMC](https://multimc.org/) or [Prism Launcher](https://prismlauncher.org/) and start the new instance. + +
+ +## Kinetic Hosting Affiliate - 15% off your first month + +Looking for an affordable and reliable hosting platform for your Minecraft server? Look no further than Kinetic Hosting! As an affiliate partner with Kinetic Hosting, we highly recommend their service. They offer fast and reliable servers with the largest range of supported modpacks. + +As a special offer for our audience, you can **[use the discount code "Skyblocker" during checkout to get 15% off your first month](https://billing.kinetichosting.net/aff.php?aff=315)**. +This helps to cover our server costs. + +[![Kinetic-hosting](https://cdn.discordapp.com/attachments/1098004599757099150/1099460202924879902/Skyblocker.png)](https://billing.kinetichosting.net/aff.php?aff=315) + +So what are you waiting for? Click on the **[link](https://billing.kinetichosting.net/aff.php?aff=315)** to check out Kinetic Hosting and start playing with your friends today! \ No newline at end of file diff --git a/build.gradle b/build.gradle index d26c0e92..8ed905d1 100644 --- a/build.gradle +++ b/build.gradle @@ -140,18 +140,22 @@ modrinth { gameVersions = [project.minecraft_version] loaders = ["fabric"] versionType = "release" - dependencies = [ // Yet another array. Create a new `ModDependency` or `VersionDependency` with two strings - the ID and the scope - new ModDependency("P7dR8mSH", "required"), // Creates a new required dependency on Fabric API - new ModDependency("mOgUt4GM", "optional"), // modmenu - new ModDependency("nfn13YXA", "optional") // REI - ] + dependencies { + required.project "fabric-api" + optional.project "modmenu" + optional.project "rei" + optional.project "emi" + } changelog = System.getenv('CHANGELOG') + syncBodyFrom = rootProject.file("MRREADME.md").text } tasks.modrinth.doLast { println "::set-output name=url::https://modrinth.com/mod/skyblocker-liap/version/$uploadInfo.id" } +tasks.modrinth.dependsOn(tasks.modrinthSyncBody) + // configure the maven publication publishing { publications { -- cgit From aed9c89764b84da3944e450e877a5707f430fe8b Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:01:06 -0400 Subject: Fix recipe book slot textures (#343) --- .../me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java index 19f656e5..61a9aa20 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java @@ -15,7 +15,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; public class ResultButtonWidget extends ClickableWidget { - private static final Identifier BACKGROUND_TEXTURE = new Identifier("textures/gui/recipe_book.png"); + private static final Identifier BACKGROUND_TEXTURE = new Identifier("recipe_book/slot_craftable"); protected ItemStack itemStack = null; @@ -38,7 +38,7 @@ public class ResultButtonWidget extends ClickableWidget { public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) { MinecraftClient client = MinecraftClient.getInstance(); // this.drawTexture(matrices, this.x, this.y, 29, 206, this.width, this.height); - context.drawTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), 29, 206, this.getWidth(), this.getHeight()); + context.drawGuiTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), this.getWidth(), this.getHeight()); // client.getItemRenderer().renderInGui(this.itemStack, this.x + 4, this.y + 4); context.drawItem(this.itemStack, this.getX() + 4, this.getY() + 4); // client.getItemRenderer().renderGuiItemOverlay(client.textRenderer, itemStack, this.x + 4, this.y + 4); -- cgit From 0c9381191663a74a9ec3cff9aae1c8b523215f0e Mon Sep 17 00:00:00 2001 From: Kevin <92656833+kevinthegreat1@users.noreply.github.com> Date: Sun, 8 Oct 2023 00:01:34 -0400 Subject: Add Support for Custom toString Function for Enum Dropdowns (#344) --- .../me/xmrvizzy/skyblocker/config/ConfigUtils.java | 12 ++--- .../skyblocker/config/SkyblockerConfig.java | 53 +++------------------- .../config/categories/DungeonsCategory.java | 19 ++++---- .../config/controllers/EnumDropdownController.java | 25 ++++++---- .../controllers/EnumDropdownControllerBuilder.java | 17 +++++++ .../EnumDropdownControllerBuilderImpl.java | 12 ++++- .../skyblock/dungeon/DungeonChestProfit.java | 2 +- .../skyblock/dungeon/DungeonChestProfitTest.java | 20 ++++---- 8 files changed, 78 insertions(+), 82 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java index 766b0643..552ed091 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java @@ -4,11 +4,15 @@ import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; import dev.isxander.yacl3.api.controller.EnumControllerBuilder; import dev.isxander.yacl3.api.controller.ValueFormatter; -import me.xmrvizzy.skyblocker.config.controllers.EnumDropdownControllerBuilder; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.StringUtils; + +import java.util.function.Function; public class ConfigUtils { - public static final ValueFormatter FLOAT_TWO_FORMATTER = value -> Text.literal(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); + public static final Function FORMATTING_TO_STRING = formatting -> StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); + public static final ValueFormatter FLOAT_TWO_FORMATTER = value -> Text.literal(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); public static BooleanControllerBuilder createBooleanController(Option opt) { return BooleanControllerBuilder.create(opt).yesNoFormatter().coloured(true); @@ -18,8 +22,4 @@ public class ConfigUtils { public static > EnumControllerBuilder createEnumCyclingListController(Option opt) { return EnumControllerBuilder.create(opt).enumClass((Class) opt.binding().defaultValue().getClass()); } - - public static > EnumDropdownControllerBuilder createEnumDropdownController(Option opt) { - return EnumDropdownControllerBuilder.create(opt); - } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java index 3cb2ce5d..a32dcbfe 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -1,10 +1,5 @@ package me.xmrvizzy.skyblocker.config; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.StringUtils; - import dev.isxander.yacl3.config.v2.api.SerialEntry; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; @@ -15,6 +10,9 @@ import net.minecraft.client.resource.language.I18n; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import java.util.ArrayList; +import java.util.List; + public class SkyblockerConfig { @SerialEntry public int version = 1; @@ -587,55 +585,18 @@ public class SkyblockerConfig { public int neutralThreshold = 1000; @SerialEntry - public FormattingOption neutralColor = FormattingOption.DARK_GRAY; + public Formatting neutralColor = Formatting.DARK_GRAY; @SerialEntry - public FormattingOption profitColor = FormattingOption.DARK_GREEN; + public Formatting profitColor = Formatting.DARK_GREEN; @SerialEntry - public FormattingOption lossColor = FormattingOption.RED; + public Formatting lossColor = Formatting.RED; @SerialEntry - public FormattingOption incompleteColor = FormattingOption.BLUE; + public Formatting incompleteColor = Formatting.BLUE; } - - public enum FormattingOption { - BLACK(Formatting.BLACK), - DARK_BLUE(Formatting.DARK_BLUE), - DARK_GREEN(Formatting.DARK_GREEN), - DARK_AQUA(Formatting.DARK_AQUA), - DARK_RED(Formatting.DARK_RED), - DARK_PURPLE(Formatting.DARK_PURPLE), - GOLD(Formatting.GOLD), - GRAY(Formatting.GRAY), - DARK_GRAY(Formatting.DARK_GRAY), - BLUE(Formatting.BLUE), - GREEN(Formatting.GREEN), - AQUA(Formatting.AQUA), - RED(Formatting.RED), - LIGHT_PURPLE(Formatting.LIGHT_PURPLE), - YELLOW(Formatting.YELLOW), - WHITE(Formatting.WHITE), - OBFUSCATED(Formatting.OBFUSCATED), - BOLD(Formatting.BOLD), - STRIKETHROUGH(Formatting.STRIKETHROUGH), - UNDERLINE(Formatting.UNDERLINE), - ITALIC(Formatting.ITALIC), - RESET(Formatting.RESET); - - public final Formatting formatting; - - - FormattingOption(Formatting formatting) { - this.formatting = formatting; - } - - @Override - public String toString() { - return StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); - } - } public static class LividColor { @SerialEntry diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java index 16439cb5..2d641ac1 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java @@ -10,11 +10,12 @@ import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; import dev.isxander.yacl3.api.controller.StringControllerBuilder; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig.FormattingOption; import me.xmrvizzy.skyblocker.config.ConfigUtils; +import me.xmrvizzy.skyblocker.config.controllers.EnumDropdownControllerBuilder; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMapConfigScreen; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; public class DungeonsCategory { @@ -150,34 +151,34 @@ public class DungeonsCategory { newValue -> config.locations.dungeons.dungeonChestProfit.neutralThreshold = newValue) .controller(IntegerFieldControllerBuilder::create) .build()) - .option(Option.createBuilder() + .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralColor")) .binding(defaults.locations.dungeons.dungeonChestProfit.neutralColor, () -> config.locations.dungeons.dungeonChestProfit.neutralColor, newValue -> config.locations.dungeons.dungeonChestProfit.neutralColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) - .option(Option.createBuilder() + .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.profitColor")) .binding(defaults.locations.dungeons.dungeonChestProfit.profitColor, () -> config.locations.dungeons.dungeonChestProfit.profitColor, newValue -> config.locations.dungeons.dungeonChestProfit.profitColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) - .option(Option.createBuilder() + .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor")) .binding(defaults.locations.dungeons.dungeonChestProfit.lossColor, () -> config.locations.dungeons.dungeonChestProfit.lossColor, newValue -> config.locations.dungeons.dungeonChestProfit.lossColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) - .option(Option.createBuilder() + .option(Option.createBuilder() .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor")) .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor.@Tooltip"))) .binding(defaults.locations.dungeons.dungeonChestProfit.incompleteColor, () -> config.locations.dungeons.dungeonChestProfit.incompleteColor, newValue -> config.locations.dungeons.dungeonChestProfit.incompleteColor = newValue) - .controller(ConfigUtils::createEnumDropdownController) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) .build()) .build()) diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java index cf40c7d5..6db0028c 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java @@ -8,16 +8,23 @@ import dev.isxander.yacl3.gui.controllers.dropdown.AbstractDropdownController; import org.jetbrains.annotations.NotNull; import java.util.Arrays; +import java.util.function.Function; import java.util.stream.Stream; public class EnumDropdownController> extends AbstractDropdownController { - protected EnumDropdownController(Option option) { + /** + * The function used to convert enum constants to strings used for display, suggestion, and validation. Defaults to {@link Enum#toString}. + */ + protected final Function toString; + + protected EnumDropdownController(Option option, Function toString) { super(option); + this.toString = toString; } @Override public String getString() { - return option().pendingValue().toString(); + return toString.apply(option().pendingValue()); } @Override @@ -26,15 +33,15 @@ public class EnumDropdownController> extends AbstractDropdownC } /** - * Searches through enum constants for one whose {@link Enum#toString()} result equals {@code value} + * Searches through enum constants for one whose {@link #toString} result equals {@code value} * * @return The enum constant associated with the {@code value} or the pending value if none are found - * @implNote The return value of {@link Enum#toString()} on each enum constant should be unique in order to ensure accuracy + * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy */ private E getEnumFromString(String value) { value = value.toLowerCase(); for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { - if (constant.toString().toLowerCase().equals(value)) return constant; + if (toString.apply(constant).toLowerCase().equals(value)) return constant; } return option().pendingValue(); @@ -44,7 +51,7 @@ public class EnumDropdownController> extends AbstractDropdownC public boolean isValueValid(String value) { value = value.toLowerCase(); for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { - if (constant.toString().equals(value)) return true; + if (toString.apply(constant).equals(value)) return true; } return false; @@ -59,16 +66,16 @@ public class EnumDropdownController> extends AbstractDropdownC } /** - * Filters and sorts through enum constants for those whose {@link Enum#toString()} result equals {@code value} + * Filters and sorts through enum constants for those whose {@link #toString} result equals {@code value} * * @return a sorted stream containing enum constants associated with the {@code value} - * @implNote The return value of {@link Enum#toString()} on each enum constant should be unique in order to ensure accuracy + * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy */ @NotNull protected Stream getValidEnumConstants(String value) { String valueLowerCase = value.toLowerCase(); return Arrays.stream(option().pendingValue().getDeclaringClass().getEnumConstants()) - .map(Enum::toString) + .map(this.toString) .filter(constant -> constant.toLowerCase().contains(valueLowerCase)) .sorted((s1, s2) -> { String s1LowerCase = s1.toLowerCase(); diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java index baadb8b3..a17f58d6 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java @@ -3,8 +3,25 @@ package me.xmrvizzy.skyblocker.config.controllers; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.controller.ControllerBuilder; +import java.util.function.Function; + public interface EnumDropdownControllerBuilder> extends ControllerBuilder { + EnumDropdownControllerBuilder toString(Function toString); + static > EnumDropdownControllerBuilder create(Option option) { return new EnumDropdownControllerBuilderImpl<>(option); } + + /** + * Creates a factory for {@link EnumDropdownControllerBuilder}s with the given function for converting enum constants to strings. + * Use this if a custom toString function for an enum is needed. + * Use it like this: + *
{@code Option.createBuilder().controller(createEnumDropdownControllerBuilder.getFactory(MY_CUSTOM_ENUM_TO_STRING_FUNCTION))}
+ * @param toString The function used to convert enum constants to strings used for display, suggestion, and validation + * @return a factory for {@link EnumDropdownControllerBuilder}s + * @param the enum type + */ + static > Function, ControllerBuilder> getFactory(Function toString) { + return opt -> EnumDropdownControllerBuilder.create(opt).toString(toString); + } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java index ea30d1da..27878c86 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java @@ -4,14 +4,24 @@ import dev.isxander.yacl3.api.Controller; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.impl.controller.AbstractControllerBuilderImpl; +import java.util.function.Function; + public class EnumDropdownControllerBuilderImpl> extends AbstractControllerBuilderImpl implements EnumDropdownControllerBuilder { + private Function toString = Enum::toString; + public EnumDropdownControllerBuilderImpl(Option option) { super(option); } + @Override + public EnumDropdownControllerBuilder toString(Function toString) { + this.toString = toString; + return this; + } + @SuppressWarnings("UnstableApiUsage") @Override public Controller build() { - return new EnumDropdownController<>(option); + return new EnumDropdownController<>(option, toString); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java index 91be5248..ea54ff32 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java @@ -160,7 +160,7 @@ public class DungeonChestProfit { private static Text getProfitText(int profit, boolean hasIncompleteData) { SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit; - return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting); + return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor); } static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) { diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java index 7a130b5a..9ff1e154 100644 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java +++ b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java @@ -8,15 +8,15 @@ public class DungeonChestProfitTest { @Test void testProfitText() { SkyblockerConfig.DungeonChestProfit config = new SkyblockerConfig.DungeonChestProfit(); - Assertions.assertEquals("literal{ 0}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(0, false, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ 0}[style={color=blue}]", DungeonChestProfit.getProfitText(0, true, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ +10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(10, false, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ +10}[style={color=blue}]", DungeonChestProfit.getProfitText(10, true, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ -10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(-10, false, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ -10}[style={color=blue}]", DungeonChestProfit.getProfitText(-10, true, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ +10,000}[style={color=dark_green}]", DungeonChestProfit.getProfitText(10000, false, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ +10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(10000, true, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ -10,000}[style={color=red}]", DungeonChestProfit.getProfitText(-10000, false, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); - Assertions.assertEquals("literal{ -10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(-10000, true, config.neutralThreshold, config.neutralColor.formatting, config.profitColor.formatting, config.lossColor.formatting, config.incompleteColor.formatting).toString()); + Assertions.assertEquals("literal{ 0}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(0, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ 0}[style={color=blue}]", DungeonChestProfit.getProfitText(0, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(10, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10}[style={color=blue}]", DungeonChestProfit.getProfitText(10, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(-10, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10}[style={color=blue}]", DungeonChestProfit.getProfitText(-10, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10,000}[style={color=dark_green}]", DungeonChestProfit.getProfitText(10000, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(10000, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10,000}[style={color=red}]", DungeonChestProfit.getProfitText(-10000, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(-10000, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); } } -- cgit From 6e5bef27ff2d78b5012f99c9d96e99d9b9673e4c Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sun, 8 Oct 2023 02:54:00 -0400 Subject: Optimize Scoreboard Stuff --- .../skyblocker/skyblock/rift/EffigyWaypoints.java | 45 ++++++-------- .../me/xmrvizzy/skyblocker/utils/SlayerUtils.java | 23 ++----- .../java/me/xmrvizzy/skyblocker/utils/Utils.java | 72 ++++++++++++++-------- 3 files changed, 71 insertions(+), 69 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java index 5548484e..4db5f3e6 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java @@ -4,13 +4,6 @@ import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; import me.xmrvizzy.skyblocker.utils.Utils; import me.xmrvizzy.skyblocker.utils.render.RenderHelper; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.scoreboard.Scoreboard; -import net.minecraft.scoreboard.ScoreboardDisplaySlot; -import net.minecraft.scoreboard.ScoreboardObjective; -import net.minecraft.scoreboard.ScoreboardPlayerScore; -import net.minecraft.scoreboard.Team; import net.minecraft.text.Text; import net.minecraft.text.TextColor; import net.minecraft.util.DyeColor; @@ -23,7 +16,7 @@ import java.util.List; public class EffigyWaypoints { private static final Logger LOGGER = LoggerFactory.getLogger(EffigyWaypoints.class); - private static final List effigies = List.of( + private static final List EFFIGIES = List.of( new BlockPos(150, 79, 95), //Effigy 1 new BlockPos(193, 93, 119), //Effigy 2 new BlockPos(235, 110, 147), //Effigy 3 @@ -31,29 +24,27 @@ public class EffigyWaypoints { new BlockPos(262, 99, 94), //Effigy 5 new BlockPos(240, 129, 118) //Effigy 6 ); - private static final List unBrokenEffigies = new ArrayList<>(); + private static final List UNBROKEN_EFFIGIES = new ArrayList<>(); protected static void updateEffigies() { if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableEffigyWaypoints || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) return; - unBrokenEffigies.clear(); + UNBROKEN_EFFIGIES.clear(); + try { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player == null) return; - Scoreboard scoreboard = player.getScoreboard(); - ScoreboardObjective objective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.FROM_ID.apply(1)); - for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) { - Team team = scoreboard.getPlayerTeam(score.getPlayerName()); - if (team != null) { - String line = team.getPrefix().getString() + team.getSuffix().getString(); - if (line.contains("Effigies")) { - List newList = new ArrayList<>(team.getPrefix().getSiblings()); - newList.addAll(team.getSuffix().getSiblings()); - for (int i = 1; i < newList.size(); i++) { - if (newList.get(i).getStyle().getColor() == TextColor.parse("gray")) { - unBrokenEffigies.add(effigies.get(i - 1)); - } - } + for (int i = 0; i < Utils.STRING_SCOREBOARD.size(); i++) { + String line = Utils.STRING_SCOREBOARD.get(i); + + if (line.contains("Effigies")) { + List effigiesText = new ArrayList<>(); + List prefixAndSuffix = Utils.TEXT_SCOREBOARD.get(i).getSiblings(); + + //Add contents of prefix and suffix to list + effigiesText.addAll(prefixAndSuffix.get(0).getSiblings()); + effigiesText.addAll(prefixAndSuffix.get(1).getSiblings()); + + for (int i2 = 1; i2 < effigiesText.size(); i2++) { + if (effigiesText.get(i2).getStyle().getColor() == TextColor.parse("gray")) UNBROKEN_EFFIGIES.add(EFFIGIES.get(i2 - 1)); } } } @@ -64,7 +55,7 @@ public class EffigyWaypoints { protected static void render(WorldRenderContext context) { if (SkyblockerConfigManager.get().slayer.vampireSlayer.enableEffigyWaypoints && Utils.getLocation().contains("Stillgore Château")) { - for (BlockPos effigy : unBrokenEffigies) { + for (BlockPos effigy : UNBROKEN_EFFIGIES) { float[] colorComponents = DyeColor.RED.getColorComponents(); if (SkyblockerConfigManager.get().slayer.vampireSlayer.compactEffigyWaypoints) { RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy.down(6), colorComponents, 0.5F); diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java index 216dc7e9..cb75f20f 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java @@ -1,14 +1,8 @@ package me.xmrvizzy.skyblocker.utils; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.scoreboard.Scoreboard; -import net.minecraft.scoreboard.ScoreboardDisplaySlot; -import net.minecraft.scoreboard.ScoreboardObjective; -import net.minecraft.scoreboard.ScoreboardPlayerScore; -import net.minecraft.scoreboard.Team; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,22 +40,15 @@ public class SlayerUtils { public static boolean isInSlayer() { try { - ClientPlayerEntity client = MinecraftClient.getInstance().player; - if (client == null) return false; - Scoreboard scoreboard = MinecraftClient.getInstance().player.getScoreboard(); - ScoreboardObjective objective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.FROM_ID.apply(1)); - for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) { - Team team = scoreboard.getPlayerTeam(score.getPlayerName()); - if (team != null) { - String line = team.getPrefix().getString() + team.getSuffix().getString(); - if (line.contains("Slay the boss!")) { - return true; - } - } + for (int i = 0; i < Utils.STRING_SCOREBOARD.size(); i++) { + String line = Utils.STRING_SCOREBOARD.get(i); + + if (line.contains("Slay the boss!")) return true; } } catch (NullPointerException e) { LOGGER.error("[Skyblocker] Error while checking if player is in slayer", e); } + return false; } } \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java index b0fb6edf..f21307b7 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java @@ -2,6 +2,8 @@ package me.xmrvizzy.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; import me.xmrvizzy.skyblocker.skyblock.rift.TheRift; import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; @@ -24,7 +26,6 @@ import net.minecraft.util.Formatting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -51,6 +52,12 @@ public class Utils { private static long clientWorldJoinTime = 0; private static boolean sentLocRaw = false; private static boolean canSendLocRaw = false; + + /** + * @implNote The parent text will always be empty, the actual text content is inside the text's siblings. + */ + public static final ObjectArrayList TEXT_SCOREBOARD = new ObjectArrayList<>(); + public static final ObjectArrayList STRING_SCOREBOARD = new ObjectArrayList<>(); public static boolean isOnHypixel() { return isOnHypixel; @@ -118,7 +125,8 @@ public class Utils { */ public static void update() { MinecraftClient client = MinecraftClient.getInstance(); - updateFromScoreboard(client); + updateScoreboard(client); + updatePlayerPresenceFromScoreboard(client); updateFromPlayerList(client); updateLocRaw(); } @@ -126,11 +134,11 @@ public class Utils { /** * Updates {@link #isOnSkyblock}, {@link #isInDungeons}, and {@link #isInjected} from the scoreboard. */ - public static void updateFromScoreboard(MinecraftClient client) { - List sidebar; + public static void updatePlayerPresenceFromScoreboard(MinecraftClient client) { + List sidebar = STRING_SCOREBOARD; FabricLoader fabricLoader = FabricLoader.getInstance(); - if ((client.world == null || client.isInSingleplayer() || (sidebar = getSidebar()) == null)) { + if ((client.world == null || client.isInSingleplayer() || sidebar == null || sidebar.isEmpty())) { if (fabricLoader.isDevelopmentEnvironment()) { sidebar = Collections.emptyList(); } else { @@ -157,12 +165,12 @@ public class Utils { SkyblockEvents.JOIN.invoker().onSkyblockJoin(); } } else { - leaveSkyblock(); + onLeaveSkyblock(); } isInDungeons = fabricLoader.isDevelopmentEnvironment() || isOnSkyblock && string.contains("The Catacombs"); } else if (isOnHypixel) { isOnHypixel = false; - leaveSkyblock(); + onLeaveSkyblock(); } } @@ -173,7 +181,7 @@ public class Utils { return serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"); } - private static void leaveSkyblock() { + private static void onLeaveSkyblock() { if (isOnSkyblock) { isOnSkyblock = false; isInDungeons = false; @@ -183,7 +191,7 @@ public class Utils { public static String getLocation() { String location = null; - List sidebarLines = getSidebar(); + List sidebarLines = STRING_SCOREBOARD; try { if (sidebarLines != null) { for (String sidebarLine : sidebarLines) { @@ -203,7 +211,7 @@ public class Utils { String purseString = null; double purse = 0; - List sidebarLines = getSidebar(); + List sidebarLines = STRING_SCOREBOARD; try { if (sidebarLines != null) { @@ -224,7 +232,7 @@ public class Utils { public static int getBits() { int bits = 0; String bitsString = null; - List sidebarLines = getSidebar(); + List sidebarLines = STRING_SCOREBOARD; try { if (sidebarLines != null) { for (String sidebarLine : sidebarLines) { @@ -240,31 +248,47 @@ public class Utils { return bits; } - public static List getSidebar() { + private static void updateScoreboard(MinecraftClient client) { try { - ClientPlayerEntity client = MinecraftClient.getInstance().player; - if (client == null) return Collections.emptyList(); - Scoreboard scoreboard = MinecraftClient.getInstance().player.getScoreboard(); + TEXT_SCOREBOARD.clear(); + STRING_SCOREBOARD.clear(); + + ClientPlayerEntity player = client.player; + if (player == null) return; + + Scoreboard scoreboard = player.getScoreboard(); ScoreboardObjective objective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.FROM_ID.apply(1)); - List lines = new ArrayList<>(); + ObjectArrayList textLines = new ObjectArrayList<>(); + ObjectArrayList stringLines = new ObjectArrayList<>(); + for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) { Team team = scoreboard.getPlayerTeam(score.getPlayerName()); + if (team != null) { - String line = team.getPrefix().getString() + team.getSuffix().getString(); - if (!line.trim().isEmpty()) { - String formatted = Formatting.strip(line); - lines.add(formatted); + Text textLine = Text.empty().append(team.getPrefix().copy()).append(team.getSuffix().copy()); + String strLine = team.getPrefix().getString() + team.getSuffix().getString(); + + if (!strLine.trim().isEmpty()) { + String formatted = Formatting.strip(strLine); + + textLines.add(textLine); + stringLines.add(formatted); } } } if (objective != null) { - lines.add(objective.getDisplayName().getString()); - Collections.reverse(lines); + stringLines.add(objective.getDisplayName().getString()); + textLines.add(Text.empty().append(objective.getDisplayName().copy())); + + Collections.reverse(stringLines); + Collections.reverse(textLines); } - return lines; + + TEXT_SCOREBOARD.addAll(textLines); + STRING_SCOREBOARD.addAll(stringLines); } catch (NullPointerException e) { - return null; + //Do nothing } } -- cgit From 6ccb02d50bcded84ab21437895c9344c9ec416cb Mon Sep 17 00:00:00 2001 From: alexia Date: Sun, 8 Oct 2023 16:21:24 +0200 Subject: Merge WrapOperations into one --- src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java index fdede686..99d42640 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java +++ b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java @@ -13,18 +13,10 @@ import net.minecraft.item.ItemStack; @Mixin(Item.class) public abstract class ItemMixin { @WrapOperation( - method = "getItemBarColor", + method = {"getItemBarColor", "getItemBarStep"}, at = @At(value = "FIELD", target = "Lnet/minecraft/item/Item;maxDamage:I", opcode = Opcodes.GETFIELD) ) - private int skyblocker$handlePickoDrillBarColor(Item item, Operation original, ItemStack stack) { - return stack.getMaxDamage(); - } - - @WrapOperation( - method = "getItemBarStep", - at = @At(value = "FIELD", target = "Lnet/minecraft/item/Item;maxDamage:I", opcode = Opcodes.GETFIELD) - ) - private int skyblocker$handlePickoDrillBarStep(Item item, Operation original, ItemStack stack) { + private int skyblocker$handlePickoDrillBar(Item item, Operation original, ItemStack stack) { return stack.getMaxDamage(); } } -- cgit From 1618ac67169cf4d8a36ce8c1238aa1e925f938aa Mon Sep 17 00:00:00 2001 From: Grayray75 <69988482+Grayray75@users.noreply.github.com> Date: Sun, 8 Oct 2023 16:43:23 +0200 Subject: Improve logs block check --- src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java index 35b520a5..dbe0c16e 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java @@ -8,7 +8,7 @@ import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.sound.BlockSoundGroup; +import net.minecraft.registry.tag.BlockTags; import net.minecraft.util.Hand; import net.minecraft.util.TypedActionResult; import net.minecraft.util.math.BlockPos; @@ -36,7 +36,7 @@ public class ItemCooldowns { String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); if (usedItemId == null) return; - if (state.getSoundGroup() == BlockSoundGroup.WOOD && state.isBurnable()) { + if (state.isIn(BlockTags.LOGS)) { if (usedItemId.equals(JUNGLE_AXE_ID)) { if (!isOnCooldown(JUNGLE_AXE_ID)) { ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); -- cgit From e9ea9fcdc69b1f039a41bac07599bc9db7e3fb67 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sun, 8 Oct 2023 11:20:56 -0400 Subject: Update loom and gradle --- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 62076 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 22 +++++++++++++--------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index 8ed905d1..3e4a13d3 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '1.3-SNAPSHOT' + id 'fabric-loom' version '1.4-SNAPSHOT' id 'maven-publish' id 'com.modrinth.minotaur' version '2.+' } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a79..7f93135c 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d3..3fa8f862 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cbb..1aa94a42 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -130,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -198,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -- cgit From bd3f0329d0e391bd84b5f9e3ff207d9dd9815853 Mon Sep 17 00:00:00 2001 From: Yasin Date: Mon, 9 Oct 2023 12:58:02 +0200 Subject: new pr because fixing merge conflict would take too long --- .github/ISSUE_TEMPLATE/crash_report.yml | 16 +- gradle.properties | 2 +- .../java/de/hysky/skyblocker/SkyblockerMod.java | 130 ++++ .../skyblocker/compatibility/MixinPlugin.java | 52 ++ .../compatibility/emi/SkyblockEmiRecipe.java | 38 + .../compatibility/emi/SkyblockerEMIPlugin.java | 29 + .../compatibility/modmenu/ModMenuEntry.java | 15 + .../compatibility/rei/SkyblockCategory.java | 84 +++ .../compatibility/rei/SkyblockCraftingDisplay.java | 40 ++ .../rei/SkyblockCraftingDisplayGenerator.java | 65 ++ .../rei/SkyblockerREIClientPlugin.java | 34 + .../de/hysky/skyblocker/config/ConfigUtils.java | 25 + .../hysky/skyblocker/config/SkyblockerConfig.java | 787 +++++++++++++++++++++ .../skyblocker/config/SkyblockerConfigManager.java | 86 +++ .../config/categories/DiscordRPCCategory.java | 49 ++ .../config/categories/DungeonsCategory.java | 316 +++++++++ .../config/categories/DwarvenMinesCategory.java | 94 +++ .../config/categories/GeneralCategory.java | 508 +++++++++++++ .../config/categories/LocationsCategory.java | 80 +++ .../config/categories/MessageFilterCategory.java | 98 +++ .../config/categories/QuickNavigationCategory.java | 605 ++++++++++++++++ .../config/categories/SlayersCategory.java | 116 +++ .../config/controllers/EnumDropdownController.java | 93 +++ .../controllers/EnumDropdownControllerBuilder.java | 27 + .../EnumDropdownControllerBuilderImpl.java | 27 + .../controllers/EnumDropdownControllerElement.java | 26 + .../events/ClientPlayerBlockBreakEvent.java | 23 + .../de/hysky/skyblocker/events/SkyblockEvents.java | 33 + .../mixin/AbstractInventoryScreenMixin.java | 19 + .../de/hysky/skyblocker/mixin/ArmorTrimMixin.java | 37 + .../de/hysky/skyblocker/mixin/BatEntityMixin.java | 21 + .../mixin/ClientPlayNetworkHandlerMixin.java | 48 ++ .../skyblocker/mixin/ClientPlayerEntityMixin.java | 35 + .../mixin/ClientPlayerInteractionManagerMixin.java | 27 + .../hysky/skyblocker/mixin/DrawContextMixin.java | 72 ++ .../hysky/skyblocker/mixin/DyeableItemMixin.java | 27 + .../hysky/skyblocker/mixin/FarmlandBlockMixin.java | 38 + .../mixin/GenericContainerScreenHandlerMixin.java | 30 + .../hysky/skyblocker/mixin/HandledScreenMixin.java | 193 +++++ .../de/hysky/skyblocker/mixin/InGameHudMixin.java | 93 +++ .../skyblocker/mixin/InventoryScreenMixin.java | 18 + .../java/de/hysky/skyblocker/mixin/ItemMixin.java | 22 + .../de/hysky/skyblocker/mixin/ItemStackMixin.java | 61 ++ .../de/hysky/skyblocker/mixin/LeverBlockMixin.java | 29 + .../skyblocker/mixin/MinecraftClientMixin.java | 25 + .../hysky/skyblocker/mixin/PlayerListHudMixin.java | 57 ++ .../skyblocker/mixin/PlayerSkinProviderMixin.java | 29 + .../de/hysky/skyblocker/mixin/ScoreboardMixin.java | 16 + .../SocialInteractionsPlayerListWidgetMixin.java | 24 + .../hysky/skyblocker/mixin/WorldRendererMixin.java | 33 + .../YggdrasilMinecraftSessionServiceMixin.java | 20 + .../mixin/YggdrasilServicesKeyInfoMixin.java | 59 ++ .../accessor/BeaconBlockEntityRendererInvoker.java | 16 + .../mixin/accessor/DrawContextInvoker.java | 17 + .../skyblocker/mixin/accessor/FrustumInvoker.java | 15 + .../mixin/accessor/HandledScreenAccessor.java | 20 + .../mixin/accessor/PlayerListHudAccessor.java | 17 + .../mixin/accessor/RecipeBookWidgetAccessor.java | 14 + .../skyblocker/mixin/accessor/ScreenAccessor.java | 14 + .../mixin/accessor/WorldRendererAccessor.java | 13 + .../de/hysky/skyblocker/skyblock/FairySouls.java | 215 ++++++ .../hysky/skyblocker/skyblock/FancyStatusBars.java | 192 +++++ .../hysky/skyblocker/skyblock/FishingHelper.java | 62 ++ .../hysky/skyblocker/skyblock/HotbarSlotLock.java | 40 ++ .../hysky/skyblocker/skyblock/QuiverWarning.java | 66 ++ .../skyblocker/skyblock/StatusBarTracker.java | 109 +++ .../hysky/skyblocker/skyblock/TeleportOverlay.java | 114 +++ .../skyblocker/skyblock/barn/HungryHiker.java | 47 ++ .../skyblocker/skyblock/barn/TreasureHunter.java | 61 ++ .../skyblocker/skyblock/dungeon/CroesusHelper.java | 34 + .../skyblocker/skyblock/dungeon/DungeonBlaze.java | 152 ++++ .../skyblock/dungeon/DungeonChestProfit.java | 169 +++++ .../skyblocker/skyblock/dungeon/DungeonMap.java | 61 ++ .../skyblock/dungeon/DungeonMapConfigScreen.java | 62 ++ .../skyblocker/skyblock/dungeon/LividColor.java | 42 ++ .../skyblocker/skyblock/dungeon/OldLever.java | 40 ++ .../hysky/skyblocker/skyblock/dungeon/Reparty.java | 94 +++ .../skyblock/dungeon/StarredMobGlow.java | 56 ++ .../skyblocker/skyblock/dungeon/ThreeWeirdos.java | 39 + .../skyblocker/skyblock/dungeon/TicTacToe.java | 136 ++++ .../hysky/skyblocker/skyblock/dungeon/Trivia.java | 100 +++ .../skyblock/dungeon/secrets/DungeonMapUtils.java | 275 +++++++ .../skyblock/dungeon/secrets/DungeonSecrets.java | 451 ++++++++++++ .../skyblocker/skyblock/dungeon/secrets/Room.java | 473 +++++++++++++ .../skyblock/dungeon/secrets/SecretWaypoint.java | 142 ++++ .../skyblock/dungeon/terminal/ColorTerminal.java | 72 ++ .../skyblock/dungeon/terminal/OrderTerminal.java | 58 ++ .../dungeon/terminal/StartsWithTerminal.java | 35 + .../skyblocker/skyblock/dwarven/DwarvenHud.java | 144 ++++ .../skyblock/dwarven/DwarvenHudConfigScreen.java | 66 ++ .../hysky/skyblocker/skyblock/dwarven/Fetchur.java | 53 ++ .../hysky/skyblocker/skyblock/dwarven/Puzzler.java | 39 + .../skyblock/experiment/ChronomatronSolver.java | 129 ++++ .../skyblock/experiment/ExperimentSolver.java | 60 ++ .../skyblock/experiment/SuperpairsSolver.java | 81 +++ .../skyblock/experiment/UltrasequencerSolver.java | 80 +++ .../skyblocker/skyblock/filters/AbilityFilter.java | 15 + .../skyblocker/skyblock/filters/AdFilter.java | 39 + .../skyblocker/skyblock/filters/AoteFilter.java | 15 + .../skyblocker/skyblock/filters/AutopetFilter.java | 35 + .../skyblocker/skyblock/filters/ComboFilter.java | 16 + .../skyblocker/skyblock/filters/HealFilter.java | 15 + .../skyblock/filters/ImplosionFilter.java | 15 + .../skyblock/filters/MoltenWaveFilter.java | 15 + .../skyblocker/skyblock/filters/ShowOffFilter.java | 18 + .../skyblock/filters/SimpleChatFilter.java | 17 + .../skyblock/filters/TeleportPadFilter.java | 16 + .../skyblocker/skyblock/item/AttributeShards.java | 59 ++ .../skyblocker/skyblock/item/BackpackPreview.java | 235 ++++++ .../skyblock/item/CompactorDeletorPreview.java | 92 +++ .../item/CompactorPreviewTooltipComponent.java | 54 ++ .../skyblock/item/CustomArmorDyeColors.java | 82 +++ .../skyblocker/skyblock/item/CustomArmorTrims.java | 154 ++++ .../skyblocker/skyblock/item/CustomItemNames.java | 74 ++ .../skyblocker/skyblock/item/ItemCooldowns.java | 115 +++ .../skyblocker/skyblock/item/ItemProtection.java | 75 ++ .../skyblock/item/ItemRarityBackgrounds.java | 109 +++ .../skyblocker/skyblock/item/PriceInfoTooltip.java | 443 ++++++++++++ .../skyblock/item/SkyblockItemRarity.java | 29 + .../hysky/skyblocker/skyblock/item/WikiLookup.java | 56 ++ .../skyblock/itemlist/ItemFixerUpper.java | 341 +++++++++ .../skyblock/itemlist/ItemListWidget.java | 102 +++ .../skyblocker/skyblock/itemlist/ItemRegistry.java | 137 ++++ .../skyblock/itemlist/ItemStackBuilder.java | 154 ++++ .../skyblock/itemlist/ResultButtonWidget.java | 65 ++ .../skyblock/itemlist/SearchResultsWidget.java | 228 ++++++ .../skyblock/itemlist/SkyblockCraftingRecipe.java | 60 ++ .../skyblocker/skyblock/quicknav/QuickNav.java | 80 +++ .../skyblock/quicknav/QuickNavButton.java | 107 +++ .../skyblocker/skyblock/rift/EffigyWaypoints.java | 71 ++ .../skyblock/rift/HealingMelonIndicator.java | 27 + .../skyblocker/skyblock/rift/ManiaIndicator.java | 42 ++ .../skyblock/rift/MirrorverseWaypoints.java | 88 +++ .../skyblocker/skyblock/rift/StakeIndicator.java | 27 + .../de/hysky/skyblocker/skyblock/rift/TheRift.java | 22 + .../skyblock/rift/TwinClawsIndicator.java | 43 ++ .../skyblocker/skyblock/shortcut/Shortcuts.java | 208 ++++++ .../shortcut/ShortcutsConfigListWidget.java | 232 ++++++ .../skyblock/shortcut/ShortcutsConfigScreen.java | 113 +++ .../skyblock/special/SpecialEffects.java | 96 +++ .../skyblocker/skyblock/spidersden/Relics.java | 171 +++++ .../hysky/skyblocker/skyblock/tabhud/TabHud.java | 39 + .../tabhud/screenbuilder/ScreenBuilder.java | 179 +++++ .../tabhud/screenbuilder/ScreenMaster.java | 144 ++++ .../tabhud/screenbuilder/pipeline/AlignStage.java | 83 +++ .../screenbuilder/pipeline/CollideStage.java | 153 ++++ .../screenbuilder/pipeline/PipelineStage.java | 14 + .../tabhud/screenbuilder/pipeline/PlaceStage.java | 94 +++ .../tabhud/screenbuilder/pipeline/StackStage.java | 114 +++ .../hysky/skyblocker/skyblock/tabhud/util/Ico.java | 60 ++ .../skyblock/tabhud/util/PlayerListMgr.java | 171 +++++ .../skyblock/tabhud/util/PlayerLocator.java | 87 +++ .../skyblock/tabhud/util/ScreenConst.java | 13 + .../tabhud/widget/CameraPositionWidget.java | 37 + .../skyblock/tabhud/widget/CommsWidget.java | 63 ++ .../skyblock/tabhud/widget/ComposterWidget.java | 30 + .../skyblock/tabhud/widget/CookieWidget.java | 50 ++ .../skyblock/tabhud/widget/DungeonBuffWidget.java | 68 ++ .../skyblock/tabhud/widget/DungeonDeathWidget.java | 47 ++ .../tabhud/widget/DungeonDownedWidget.java | 44 ++ .../tabhud/widget/DungeonPlayerWidget.java | 103 +++ .../tabhud/widget/DungeonPuzzleWidget.java | 57 ++ .../tabhud/widget/DungeonSecretWidget.java | 26 + .../tabhud/widget/DungeonServerWidget.java | 48 ++ .../skyblock/tabhud/widget/EffectWidget.java | 67 ++ .../skyblock/tabhud/widget/ElectionWidget.java | 104 +++ .../skyblock/tabhud/widget/ErrorWidget.java | 32 + .../skyblock/tabhud/widget/EssenceWidget.java | 47 ++ .../skyblock/tabhud/widget/EventWidget.java | 35 + .../skyblock/tabhud/widget/FireSaleWidget.java | 68 ++ .../skyblock/tabhud/widget/ForgeWidget.java | 81 +++ .../skyblock/tabhud/widget/GardenServerWidget.java | 54 ++ .../skyblock/tabhud/widget/GardenSkillsWidget.java | 80 +++ .../tabhud/widget/GardenVisitorsWidget.java | 30 + .../skyblock/tabhud/widget/GuestServerWidget.java | 30 + .../skyblock/tabhud/widget/IslandGuestsWidget.java | 47 ++ .../skyblock/tabhud/widget/IslandOwnersWidget.java | 66 ++ .../skyblock/tabhud/widget/IslandSelfWidget.java | 43 ++ .../skyblock/tabhud/widget/IslandServerWidget.java | 32 + .../tabhud/widget/JacobsContestWidget.java | 62 ++ .../skyblock/tabhud/widget/MinionWidget.java | 151 ++++ .../skyblock/tabhud/widget/ParkServerWidget.java | 30 + .../skyblock/tabhud/widget/PlayerListWidget.java | 71 ++ .../skyblock/tabhud/widget/PowderWidget.java | 29 + .../skyblock/tabhud/widget/ProfileWidget.java | 28 + .../skyblock/tabhud/widget/QuestWidget.java | 33 + .../skyblock/tabhud/widget/ReputationWidget.java | 69 ++ .../skyblock/tabhud/widget/ServerWidget.java | 30 + .../skyblock/tabhud/widget/SkillsWidget.java | 78 ++ .../skyblock/tabhud/widget/TrapperWidget.java | 25 + .../skyblock/tabhud/widget/UpgradeWidget.java | 51 ++ .../skyblock/tabhud/widget/VolcanoWidget.java | 59 ++ .../skyblocker/skyblock/tabhud/widget/Widget.java | 216 ++++++ .../tabhud/widget/component/Component.java | 31 + .../widget/component/IcoFatTextComponent.java | 45 ++ .../tabhud/widget/component/IcoTextComponent.java | 40 ++ .../widget/component/PlainTextComponent.java | 30 + .../tabhud/widget/component/PlayerComponent.java | 39 + .../tabhud/widget/component/ProgressComponent.java | 69 ++ .../tabhud/widget/component/TableComponent.java | 58 ++ .../skyblock/tabhud/widget/hud/HudCommsWidget.java | 73 ++ .../tabhud/widget/rift/AdvertisementWidget.java | 35 + .../tabhud/widget/rift/GoodToKnowWidget.java | 69 ++ .../tabhud/widget/rift/RiftProfileWidget.java | 21 + .../tabhud/widget/rift/RiftProgressWidget.java | 123 ++++ .../tabhud/widget/rift/RiftServerInfoWidget.java | 27 + .../tabhud/widget/rift/RiftStatsWidget.java | 43 ++ .../skyblock/tabhud/widget/rift/ShenWidget.java | 22 + src/main/java/de/hysky/skyblocker/utils/Boxes.java | 50 ++ .../java/de/hysky/skyblocker/utils/Constants.java | 8 + src/main/java/de/hysky/skyblocker/utils/Http.java | 89 +++ .../java/de/hysky/skyblocker/utils/ItemUtils.java | 111 +++ .../java/de/hysky/skyblocker/utils/NEURepo.java | 101 +++ .../java/de/hysky/skyblocker/utils/PosUtils.java | 14 + .../de/hysky/skyblocker/utils/SlayerUtils.java | 54 ++ src/main/java/de/hysky/skyblocker/utils/Utils.java | 370 ++++++++++ .../skyblocker/utils/chat/ChatFilterResult.java | 18 + .../skyblocker/utils/chat/ChatMessageListener.java | 89 +++ .../skyblocker/utils/chat/ChatPatternListener.java | 30 + .../utils/discord/DiscordRPCManager.java | 122 ++++ .../skyblocker/utils/render/FrustumUtils.java | 21 + .../skyblocker/utils/render/RenderHelper.java | 247 +++++++ .../utils/render/culling/OcclusionCulling.java | 47 ++ .../utils/render/culling/WorldProvider.java | 28 + .../utils/render/culling/package-info.java | 4 + .../utils/render/gui/ColorHighlight.java | 24 + .../utils/render/gui/ContainerSolver.java | 44 ++ .../utils/render/gui/ContainerSolverManager.java | 125 ++++ .../hysky/skyblocker/utils/render/title/Title.java | 53 ++ .../utils/render/title/TitleContainer.java | 175 +++++ .../render/title/TitleContainerConfigScreen.java | 170 +++++ .../utils/scheduler/MessageScheduler.java | 66 ++ .../skyblocker/utils/scheduler/Scheduler.java | 140 ++++ .../skyblocker/utils/tictactoe/TicTacToeUtils.java | 104 +++ .../java/me/xmrvizzy/skyblocker/SkyblockerMod.java | 128 ---- .../skyblocker/compatibility/MixinPlugin.java | 52 -- .../compatibility/emi/SkyblockEmiRecipe.java | 38 - .../compatibility/emi/SkyblockerEMIPlugin.java | 29 - .../compatibility/modmenu/ModMenuEntry.java | 15 - .../compatibility/rei/SkyblockCategory.java | 84 --- .../compatibility/rei/SkyblockCraftingDisplay.java | 40 -- .../rei/SkyblockCraftingDisplayGenerator.java | 65 -- .../rei/SkyblockerREIClientPlugin.java | 34 - .../me/xmrvizzy/skyblocker/config/ConfigUtils.java | 25 - .../skyblocker/config/SkyblockerConfig.java | 787 --------------------- .../skyblocker/config/SkyblockerConfigManager.java | 86 --- .../config/categories/DiscordRPCCategory.java | 49 -- .../config/categories/DungeonsCategory.java | 316 --------- .../config/categories/DwarvenMinesCategory.java | 94 --- .../config/categories/GeneralCategory.java | 508 ------------- .../config/categories/LocationsCategory.java | 80 --- .../config/categories/MessageFilterCategory.java | 98 --- .../config/categories/QuickNavigationCategory.java | 605 ---------------- .../config/categories/SlayersCategory.java | 116 --- .../config/controllers/EnumDropdownController.java | 93 --- .../controllers/EnumDropdownControllerBuilder.java | 27 - .../EnumDropdownControllerBuilderImpl.java | 27 - .../controllers/EnumDropdownControllerElement.java | 26 - .../events/ClientPlayerBlockBreakEvent.java | 23 - .../xmrvizzy/skyblocker/events/SkyblockEvents.java | 33 - .../mixin/AbstractInventoryScreenMixin.java | 19 - .../xmrvizzy/skyblocker/mixin/ArmorTrimMixin.java | 37 - .../xmrvizzy/skyblocker/mixin/BatEntityMixin.java | 21 - .../mixin/ClientPlayNetworkHandlerMixin.java | 48 -- .../skyblocker/mixin/ClientPlayerEntityMixin.java | 35 - .../mixin/ClientPlayerInteractionManagerMixin.java | 27 - .../skyblocker/mixin/DrawContextMixin.java | 73 -- .../skyblocker/mixin/DyeableItemMixin.java | 27 - .../skyblocker/mixin/FarmlandBlockMixin.java | 38 - .../mixin/GenericContainerScreenHandlerMixin.java | 30 - .../skyblocker/mixin/HandledScreenMixin.java | 193 ----- .../xmrvizzy/skyblocker/mixin/InGameHudMixin.java | 93 --- .../skyblocker/mixin/InventoryScreenMixin.java | 18 - .../me/xmrvizzy/skyblocker/mixin/ItemMixin.java | 22 - .../xmrvizzy/skyblocker/mixin/ItemStackMixin.java | 62 -- .../xmrvizzy/skyblocker/mixin/LeverBlockMixin.java | 29 - .../skyblocker/mixin/MinecraftClientMixin.java | 25 - .../skyblocker/mixin/PlayerListHudMixin.java | 57 -- .../skyblocker/mixin/PlayerSkinProviderMixin.java | 29 - .../xmrvizzy/skyblocker/mixin/ScoreboardMixin.java | 16 - .../SocialInteractionsPlayerListWidgetMixin.java | 24 - .../skyblocker/mixin/WorldRendererMixin.java | 33 - .../YggdrasilMinecraftSessionServiceMixin.java | 20 - .../mixin/YggdrasilServicesKeyInfoMixin.java | 59 -- .../accessor/BeaconBlockEntityRendererInvoker.java | 16 - .../mixin/accessor/DrawContextInvoker.java | 17 - .../skyblocker/mixin/accessor/FrustumInvoker.java | 14 - .../mixin/accessor/HandledScreenAccessor.java | 20 - .../mixin/accessor/PlayerListHudAccessor.java | 17 - .../mixin/accessor/RecipeBookWidgetAccessor.java | 14 - .../skyblocker/mixin/accessor/ScreenAccessor.java | 14 - .../mixin/accessor/WorldRendererAccessor.java | 13 - .../xmrvizzy/skyblocker/skyblock/FairySouls.java | 215 ------ .../skyblocker/skyblock/FancyStatusBars.java | 192 ----- .../skyblocker/skyblock/FishingHelper.java | 62 -- .../skyblocker/skyblock/HotbarSlotLock.java | 40 -- .../skyblocker/skyblock/QuiverWarning.java | 66 -- .../skyblocker/skyblock/StatusBarTracker.java | 109 --- .../skyblocker/skyblock/TeleportOverlay.java | 114 --- .../skyblocker/skyblock/barn/HungryHiker.java | 47 -- .../skyblocker/skyblock/barn/TreasureHunter.java | 61 -- .../skyblocker/skyblock/dungeon/CroesusHelper.java | 34 - .../skyblocker/skyblock/dungeon/DungeonBlaze.java | 152 ---- .../skyblock/dungeon/DungeonChestProfit.java | 169 ----- .../skyblocker/skyblock/dungeon/DungeonMap.java | 61 -- .../skyblock/dungeon/DungeonMapConfigScreen.java | 62 -- .../skyblocker/skyblock/dungeon/LividColor.java | 42 -- .../skyblocker/skyblock/dungeon/OldLever.java | 40 -- .../skyblocker/skyblock/dungeon/Reparty.java | 94 --- .../skyblock/dungeon/StarredMobGlow.java | 56 -- .../skyblocker/skyblock/dungeon/ThreeWeirdos.java | 39 - .../skyblocker/skyblock/dungeon/TicTacToe.java | 136 ---- .../skyblocker/skyblock/dungeon/Trivia.java | 100 --- .../skyblock/dungeon/secrets/DungeonMapUtils.java | 275 ------- .../skyblock/dungeon/secrets/DungeonSecrets.java | 451 ------------ .../skyblocker/skyblock/dungeon/secrets/Room.java | 473 ------------- .../skyblock/dungeon/secrets/SecretWaypoint.java | 142 ---- .../skyblock/dungeon/terminal/ColorTerminal.java | 72 -- .../skyblock/dungeon/terminal/OrderTerminal.java | 58 -- .../dungeon/terminal/StartsWithTerminal.java | 35 - .../skyblocker/skyblock/dwarven/DwarvenHud.java | 144 ---- .../skyblock/dwarven/DwarvenHudConfigScreen.java | 67 -- .../skyblocker/skyblock/dwarven/Fetchur.java | 53 -- .../skyblocker/skyblock/dwarven/Puzzler.java | 39 - .../skyblock/experiment/ChronomatronSolver.java | 129 ---- .../skyblock/experiment/ExperimentSolver.java | 60 -- .../skyblock/experiment/SuperpairsSolver.java | 81 --- .../skyblock/experiment/UltrasequencerSolver.java | 80 --- .../skyblocker/skyblock/filters/AbilityFilter.java | 15 - .../skyblocker/skyblock/filters/AdFilter.java | 39 - .../skyblocker/skyblock/filters/AoteFilter.java | 15 - .../skyblocker/skyblock/filters/AutopetFilter.java | 35 - .../skyblocker/skyblock/filters/ComboFilter.java | 16 - .../skyblocker/skyblock/filters/HealFilter.java | 15 - .../skyblock/filters/ImplosionFilter.java | 15 - .../skyblock/filters/MoltenWaveFilter.java | 15 - .../skyblocker/skyblock/filters/ShowOffFilter.java | 18 - .../skyblock/filters/SimpleChatFilter.java | 17 - .../skyblock/filters/TeleportPadFilter.java | 16 - .../skyblocker/skyblock/item/AttributeShards.java | 59 -- .../skyblocker/skyblock/item/BackpackPreview.java | 235 ------ .../skyblock/item/CompactorDeletorPreview.java | 92 --- .../item/CompactorPreviewTooltipComponent.java | 54 -- .../skyblock/item/CustomArmorDyeColors.java | 82 --- .../skyblocker/skyblock/item/CustomArmorTrims.java | 154 ---- .../skyblocker/skyblock/item/CustomItemNames.java | 74 -- .../skyblocker/skyblock/item/ItemCooldowns.java | 115 --- .../skyblocker/skyblock/item/ItemProtection.java | 75 -- .../skyblock/item/ItemRarityBackgrounds.java | 109 --- .../skyblocker/skyblock/item/PriceInfoTooltip.java | 443 ------------ .../skyblock/item/SkyblockItemRarity.java | 29 - .../skyblocker/skyblock/item/WikiLookup.java | 56 -- .../skyblock/itemlist/ItemFixerUpper.java | 341 --------- .../skyblock/itemlist/ItemListWidget.java | 102 --- .../skyblocker/skyblock/itemlist/ItemRegistry.java | 137 ---- .../skyblock/itemlist/ItemStackBuilder.java | 154 ---- .../skyblock/itemlist/ResultButtonWidget.java | 65 -- .../skyblock/itemlist/SearchResultsWidget.java | 228 ------ .../skyblock/itemlist/SkyblockCraftingRecipe.java | 60 -- .../skyblocker/skyblock/quicknav/QuickNav.java | 80 --- .../skyblock/quicknav/QuickNavButton.java | 107 --- .../skyblocker/skyblock/rift/EffigyWaypoints.java | 71 -- .../skyblock/rift/HealingMelonIndicator.java | 27 - .../skyblocker/skyblock/rift/ManiaIndicator.java | 42 -- .../skyblock/rift/MirrorverseWaypoints.java | 88 --- .../skyblocker/skyblock/rift/StakeIndicator.java | 27 - .../xmrvizzy/skyblocker/skyblock/rift/TheRift.java | 21 - .../skyblock/rift/TwinClawsIndicator.java | 43 -- .../skyblocker/skyblock/shortcut/Shortcuts.java | 208 ------ .../shortcut/ShortcutsConfigListWidget.java | 232 ------ .../skyblock/shortcut/ShortcutsConfigScreen.java | 113 --- .../skyblock/special/SpecialEffects.java | 96 --- .../skyblocker/skyblock/spidersden/Relics.java | 171 ----- .../skyblocker/skyblock/tabhud/TabHud.java | 39 - .../tabhud/screenbuilder/ScreenBuilder.java | 179 ----- .../tabhud/screenbuilder/ScreenMaster.java | 144 ---- .../tabhud/screenbuilder/pipeline/AlignStage.java | 83 --- .../screenbuilder/pipeline/CollideStage.java | 153 ---- .../screenbuilder/pipeline/PipelineStage.java | 14 - .../tabhud/screenbuilder/pipeline/PlaceStage.java | 94 --- .../tabhud/screenbuilder/pipeline/StackStage.java | 114 --- .../skyblocker/skyblock/tabhud/util/Ico.java | 60 -- .../skyblock/tabhud/util/PlayerListMgr.java | 172 ----- .../skyblock/tabhud/util/PlayerLocator.java | 87 --- .../skyblock/tabhud/util/ScreenConst.java | 13 - .../tabhud/widget/CameraPositionWidget.java | 37 - .../skyblock/tabhud/widget/CommsWidget.java | 63 -- .../skyblock/tabhud/widget/ComposterWidget.java | 30 - .../skyblock/tabhud/widget/CookieWidget.java | 50 -- .../skyblock/tabhud/widget/DungeonBuffWidget.java | 68 -- .../skyblock/tabhud/widget/DungeonDeathWidget.java | 47 -- .../tabhud/widget/DungeonDownedWidget.java | 44 -- .../tabhud/widget/DungeonPlayerWidget.java | 103 --- .../tabhud/widget/DungeonPuzzleWidget.java | 57 -- .../tabhud/widget/DungeonSecretWidget.java | 26 - .../tabhud/widget/DungeonServerWidget.java | 48 -- .../skyblock/tabhud/widget/EffectWidget.java | 67 -- .../skyblock/tabhud/widget/ElectionWidget.java | 104 --- .../skyblock/tabhud/widget/ErrorWidget.java | 32 - .../skyblock/tabhud/widget/EssenceWidget.java | 47 -- .../skyblock/tabhud/widget/EventWidget.java | 35 - .../skyblock/tabhud/widget/FireSaleWidget.java | 68 -- .../skyblock/tabhud/widget/ForgeWidget.java | 81 --- .../skyblock/tabhud/widget/GardenServerWidget.java | 54 -- .../skyblock/tabhud/widget/GardenSkillsWidget.java | 80 --- .../tabhud/widget/GardenVisitorsWidget.java | 30 - .../skyblock/tabhud/widget/GuestServerWidget.java | 30 - .../skyblock/tabhud/widget/IslandGuestsWidget.java | 47 -- .../skyblock/tabhud/widget/IslandOwnersWidget.java | 66 -- .../skyblock/tabhud/widget/IslandSelfWidget.java | 43 -- .../skyblock/tabhud/widget/IslandServerWidget.java | 32 - .../tabhud/widget/JacobsContestWidget.java | 62 -- .../skyblock/tabhud/widget/MinionWidget.java | 151 ---- .../skyblock/tabhud/widget/ParkServerWidget.java | 30 - .../skyblock/tabhud/widget/PlayerListWidget.java | 71 -- .../skyblock/tabhud/widget/PowderWidget.java | 29 - .../skyblock/tabhud/widget/ProfileWidget.java | 28 - .../skyblock/tabhud/widget/QuestWidget.java | 33 - .../skyblock/tabhud/widget/ReputationWidget.java | 69 -- .../skyblock/tabhud/widget/ServerWidget.java | 30 - .../skyblock/tabhud/widget/SkillsWidget.java | 78 -- .../skyblock/tabhud/widget/TrapperWidget.java | 25 - .../skyblock/tabhud/widget/UpgradeWidget.java | 51 -- .../skyblock/tabhud/widget/VolcanoWidget.java | 59 -- .../skyblocker/skyblock/tabhud/widget/Widget.java | 216 ------ .../tabhud/widget/component/Component.java | 31 - .../widget/component/IcoFatTextComponent.java | 45 -- .../tabhud/widget/component/IcoTextComponent.java | 40 -- .../widget/component/PlainTextComponent.java | 30 - .../tabhud/widget/component/PlayerComponent.java | 39 - .../tabhud/widget/component/ProgressComponent.java | 69 -- .../tabhud/widget/component/TableComponent.java | 58 -- .../skyblock/tabhud/widget/hud/HudCommsWidget.java | 73 -- .../tabhud/widget/rift/AdvertisementWidget.java | 35 - .../tabhud/widget/rift/GoodToKnowWidget.java | 69 -- .../tabhud/widget/rift/RiftProfileWidget.java | 21 - .../tabhud/widget/rift/RiftProgressWidget.java | 123 ---- .../tabhud/widget/rift/RiftServerInfoWidget.java | 27 - .../tabhud/widget/rift/RiftStatsWidget.java | 43 -- .../skyblock/tabhud/widget/rift/ShenWidget.java | 22 - .../java/me/xmrvizzy/skyblocker/utils/Boxes.java | 50 -- .../me/xmrvizzy/skyblocker/utils/Constants.java | 8 - .../java/me/xmrvizzy/skyblocker/utils/Http.java | 89 --- .../me/xmrvizzy/skyblocker/utils/ItemUtils.java | 111 --- .../java/me/xmrvizzy/skyblocker/utils/NEURepo.java | 101 --- .../me/xmrvizzy/skyblocker/utils/PosUtils.java | 14 - .../me/xmrvizzy/skyblocker/utils/SlayerUtils.java | 54 -- .../java/me/xmrvizzy/skyblocker/utils/Utils.java | 370 ---------- .../skyblocker/utils/chat/ChatFilterResult.java | 18 - .../skyblocker/utils/chat/ChatMessageListener.java | 88 --- .../skyblocker/utils/chat/ChatPatternListener.java | 30 - .../utils/discord/DiscordRPCManager.java | 121 ---- .../skyblocker/utils/render/FrustumUtils.java | 21 - .../skyblocker/utils/render/RenderHelper.java | 247 ------- .../utils/render/culling/OcclusionCulling.java | 47 -- .../utils/render/culling/WorldProvider.java | 28 - .../utils/render/culling/package-info.java | 4 - .../utils/render/gui/ColorHighlight.java | 24 - .../utils/render/gui/ContainerSolver.java | 44 -- .../utils/render/gui/ContainerSolverManager.java | 125 ---- .../skyblocker/utils/render/title/Title.java | 53 -- .../utils/render/title/TitleContainer.java | 175 ----- .../render/title/TitleContainerConfigScreen.java | 170 ----- .../utils/scheduler/MessageScheduler.java | 66 -- .../skyblocker/utils/scheduler/Scheduler.java | 140 ---- .../skyblocker/utils/tictactoe/TicTacToeUtils.java | 104 --- src/main/resources/fabric.mod.json | 8 +- src/main/resources/skyblocker.mixins.json | 4 +- .../skyblocker/skyblock/StatusBarTrackerTest.java | 74 ++ .../skyblock/dungeon/AcceptRepartyTest.java | 37 + .../skyblock/dungeon/DungeonChestProfitTest.java | 22 + .../skyblock/dungeon/ThreeWeirdosTest.java | 19 + .../skyblocker/skyblock/dungeon/TriviaTest.java | 33 + .../skyblock/dungeon/secrets/DungeonRoomsDFU.java | 167 +++++ .../skyblock/dungeon/secrets/RoomTest.java | 13 + .../skyblocker/skyblock/dwarven/FetchurTest.java | 15 + .../skyblocker/skyblock/dwarven/PuzzlerTest.java | 15 + .../skyblock/filters/AbilityFilterTest.java | 19 + .../skyblocker/skyblock/filters/AdFilterTest.java | 68 ++ .../skyblock/filters/AoteFilterTest.java | 14 + .../skyblock/filters/AutopetFilterTest.java | 15 + .../skyblock/filters/ChatFilterTest.java | 10 + .../skyblock/filters/ComboFilterTest.java | 29 + .../skyblock/filters/HealFilterTest.java | 19 + .../skyblock/filters/ImplosionFilterTest.java | 19 + .../skyblock/filters/TeleportPadFilterTest.java | 19 + .../item/ArmorTrimIdSerializationTest.java | 27 + .../utils/chat/ChatPatternListenerTest.java | 28 + .../skyblocker/utils/scheduler/SchedulerTest.java | 88 +++ .../skyblocker/skyblock/StatusBarTrackerTest.java | 74 -- .../skyblock/dungeon/AcceptRepartyTest.java | 37 - .../skyblock/dungeon/DungeonChestProfitTest.java | 22 - .../skyblock/dungeon/ThreeWeirdosTest.java | 19 - .../skyblocker/skyblock/dungeon/TriviaTest.java | 33 - .../skyblock/dungeon/secrets/DungeonRoomsDFU.java | 167 ----- .../skyblock/dungeon/secrets/RoomTest.java | 13 - .../skyblocker/skyblock/dwarven/FetchurTest.java | 15 - .../skyblocker/skyblock/dwarven/PuzzlerTest.java | 15 - .../skyblock/filters/AbilityFilterTest.java | 19 - .../skyblocker/skyblock/filters/AdFilterTest.java | 68 -- .../skyblock/filters/AoteFilterTest.java | 14 - .../skyblock/filters/AutopetFilterTest.java | 15 - .../skyblock/filters/ChatFilterTest.java | 10 - .../skyblock/filters/ComboFilterTest.java | 29 - .../skyblock/filters/HealFilterTest.java | 19 - .../skyblock/filters/ImplosionFilterTest.java | 19 - .../skyblock/filters/TeleportPadFilterTest.java | 19 - .../item/ArmorTrimIdSerializationTest.java | 27 - .../utils/chat/ChatPatternListenerTest.java | 28 - .../skyblocker/utils/scheduler/SchedulerTest.java | 88 --- 510 files changed, 20161 insertions(+), 20159 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/SkyblockerMod.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/MixinPlugin.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/modmenu/ModMenuEntry.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java create mode 100644 src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java create mode 100644 src/main/java/de/hysky/skyblocker/config/ConfigUtils.java create mode 100644 src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java create mode 100644 src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/DiscordRPCCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java create mode 100644 src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownController.java create mode 100644 src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilder.java create mode 100644 src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java create mode 100644 src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerElement.java create mode 100644 src/main/java/de/hysky/skyblocker/events/ClientPlayerBlockBreakEvent.java create mode 100644 src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/AbstractInventoryScreenMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/DrawContextMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/DyeableItemMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/FarmlandBlockMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/GenericContainerScreenHandlerMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/InventoryScreenMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ItemMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/LeverBlockMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/PlayerListHudMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/PlayerSkinProviderMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/ScoreboardMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/WorldRendererMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/HandledScreenAccessor.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/PlayerListHudAccessor.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/ScreenAccessor.java create mode 100644 src/main/java/de/hysky/skyblocker/mixin/accessor/WorldRendererAccessor.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/FairySouls.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/FishingHelper.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/QuiverWarning.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/StatusBarTracker.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/barn/HungryHiker.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/barn/TreasureHunter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/LividColor.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/OldLever.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/Reparty.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/StarredMobGlow.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dwarven/Fetchur.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/dwarven/Puzzler.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/AdFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/AoteFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/AutopetFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/ComboFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/ShowOffFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilter.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/AttributeShards.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockItemRarity.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRegistry.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ResultButtonWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/EffigyWaypoints.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/HealingMelonIndicator.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/ManiaIndicator.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/MirrorverseWaypoints.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/StakeIndicator.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/rift/TwinClawsIndicator.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/shortcut/Shortcuts.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/spidersden/Relics.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/Boxes.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/Constants.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/Http.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/ItemUtils.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/NEURepo.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/PosUtils.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/Utils.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/chat/ChatFilterResult.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/FrustumUtils.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/culling/OcclusionCulling.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/culling/WorldProvider.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/culling/package-info.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/ColorHighlight.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/title/Title.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainer.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainerConfigScreen.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/scheduler/MessageScheduler.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/scheduler/Scheduler.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/tictactoe/TicTacToeUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockEmiRecipe.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/modmenu/ModMenuEntry.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfigManager.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/DiscordRPCCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/DwarvenMinesCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/LocationsCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/MessageFilterCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/QuickNavigationCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/categories/SlayersCategory.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerElement.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ArmorTrimMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/DyeableItemMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenHandlerMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerSkinProviderMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/ScoreboardMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/HandledScreenAccessor.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/PlayerListHudAccessor.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/WorldRendererAccessor.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/HotbarSlotLock.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/TeleportOverlay.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/HungryHiker.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/TreasureHunter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Fetchur.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Puzzler.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/MoltenWaveFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/SimpleChatFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilter.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorDyeColors.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemProtection.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemFixerUpper.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ErrorWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/Boxes.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/Http.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatFilterResult.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatMessageListener.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListener.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/FrustumUtils.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/OcclusionCulling.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/WorldProvider.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/package-info.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ColorHighlight.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolver.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolverManager.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/title/Title.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainerConfigScreen.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java delete mode 100644 src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/StatusBarTrackerTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/AcceptRepartyTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/TriviaTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/RoomTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dwarven/FetchurTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/dwarven/PuzzlerTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/AbilityFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/AdFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/AoteFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/AutopetFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/ComboFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/HealFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilterTest.java create mode 100644 src/test/java/de/hysky/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java create mode 100644 src/test/java/de/hysky/skyblocker/utils/chat/ChatPatternListenerTest.java create mode 100644 src/test/java/de/hysky/skyblocker/utils/scheduler/SchedulerTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTrackerTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TriviaTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/FetchurTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/PuzzlerTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ChatFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilterTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListenerTest.java delete mode 100644 src/test/java/me/xmrvizzy/skyblocker/utils/scheduler/SchedulerTest.java diff --git a/.github/ISSUE_TEMPLATE/crash_report.yml b/.github/ISSUE_TEMPLATE/crash_report.yml index 825ddb3d..d3433bd0 100644 --- a/.github/ISSUE_TEMPLATE/crash_report.yml +++ b/.github/ISSUE_TEMPLATE/crash_report.yml @@ -55,14 +55,14 @@ body: Description: Unexpected error java.lang.NullPointerException: Cannot invoke "java.util.HashMap.get(Object)" because the return value of "java.util.HashMap.get(Object)" is null - at me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.Room.(Room.java:80) - at me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets.newRoom(DungeonSecrets.java:297) - at me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets.update(DungeonSecrets.java:263) - at me.xmrvizzy.skyblocker.utils.Scheduler$CyclicTask.run(Scheduler.java:102) - at me.xmrvizzy.skyblocker.utils.Scheduler$ScheduledTask.run(Scheduler.java:120) - at me.xmrvizzy.skyblocker.utils.Scheduler.runTask(Scheduler.java:88) - at me.xmrvizzy.skyblocker.utils.Scheduler.tick(Scheduler.java:76) - at me.xmrvizzy.skyblocker.SkyblockerMod.tick(SkyblockerMod.java:116) + at de.hysky.skyblocker.skyblock.dungeon.secrets.Room.(Room.java:80) + at de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets.newRoom(DungeonSecrets.java:297) + at de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets.update(DungeonSecrets.java:263) + at de.hysky.skyblocker.utils.Scheduler$CyclicTask.run(Scheduler.java:102) + at de.hysky.skyblocker.utils.Scheduler$ScheduledTask.run(Scheduler.java:120) + at de.hysky.skyblocker.utils.Scheduler.runTask(Scheduler.java:88) + at de.hysky.skyblocker.utils.Scheduler.tick(Scheduler.java:76) + at de.hysky.skyblocker.SkyblockerMod.tick(SkyblockerMod.java:116) at net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents.lambda$static$2(ClientTickEvents.java:43) at net.minecraft.class_310.handler$ble000$fabric-lifecycle-events-v1$onEndTick(class_310.java:11022) at net.minecraft.class_310.method_1574(class_310.java:1957) diff --git a/gradle.properties b/gradle.properties index 44dbbc41..7b749d92 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,6 +34,6 @@ repoparser_version = 1.3.2 # Mod Properties mod_version = 1.14.0 -maven_group = me.xmrvizzy +maven_group = de.hysky archives_base_name = skyblocker modrinth_id=y6DuFGwJ \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java new file mode 100644 index 00000000..2cf46706 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -0,0 +1,130 @@ +package de.hysky.skyblocker; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import de.hysky.skyblocker.skyblock.*; +import de.hysky.skyblocker.skyblock.dungeon.*; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; +import de.hysky.skyblocker.skyblock.item.*; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.*; +import de.hysky.skyblocker.skyblock.dungeon.*; +import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud; +import de.hysky.skyblocker.skyblock.item.*; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import de.hysky.skyblocker.skyblock.quicknav.QuickNav; +import de.hysky.skyblocker.skyblock.rift.TheRift; +import de.hysky.skyblocker.skyblock.shortcut.Shortcuts; +import de.hysky.skyblocker.skyblock.special.SpecialEffects; +import de.hysky.skyblocker.skyblock.spidersden.Relics; +import de.hysky.skyblocker.skyblock.tabhud.TabHud; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.utils.NEURepo; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.chat.ChatMessageListener; +import de.hysky.skyblocker.utils.discord.DiscordRPCManager; +import de.hysky.skyblocker.utils.render.culling.OcclusionCulling; +import de.hysky.skyblocker.utils.render.gui.ContainerSolverManager; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; + +import java.nio.file.Path; + +/** + * Main class for Skyblocker which initializes features, registers events, and + * manages ticks. This class will be instantiated by Fabric. Do not instantiate + * this class. + */ +public class SkyblockerMod implements ClientModInitializer { + public static final String VERSION = FabricLoader.getInstance().getModContainer("skyblocker").get().getMetadata().getVersion().getFriendlyString(); + public static final String NAMESPACE = "skyblocker"; + public static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir().resolve(NAMESPACE); + public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static SkyblockerMod INSTANCE; + public final ContainerSolverManager containerSolverManager = new ContainerSolverManager(); + public final StatusBarTracker statusBarTracker = new StatusBarTracker(); + + /** + * Do not instantiate this class. Use {@link #getInstance()} instead. + */ + @Deprecated + public SkyblockerMod() { + INSTANCE = this; + } + + public static SkyblockerMod getInstance() { + return INSTANCE; + } + + /** + * Register {@link #tick(MinecraftClient)} to + * {@link ClientTickEvents#END_CLIENT_TICK}, initialize all features, and + * schedule tick events. + */ + @Override + public void onInitializeClient() { + ClientTickEvents.END_CLIENT_TICK.register(this::tick); + Utils.init(); + HotbarSlotLock.init(); + SkyblockerConfigManager.init(); + PriceInfoTooltip.init(); + WikiLookup.init(); + ItemRegistry.init(); + NEURepo.init(); + FairySouls.init(); + Relics.init(); + BackpackPreview.init(); + QuickNav.init(); + ItemCooldowns.init(); + DwarvenHud.init(); + ChatMessageListener.init(); + Shortcuts.init(); + DiscordRPCManager.init(); + LividColor.init(); + FishingHelper.init(); + TabHud.init(); + DungeonMap.init(); + DungeonSecrets.init(); + DungeonBlaze.init(); + DungeonChestProfit.init(); + TheRift.init(); + TitleContainer.init(); + ScreenMaster.init(); + OcclusionCulling.init(); + TeleportOverlay.init(); + CustomItemNames.init(); + CustomArmorDyeColors.init(); + CustomArmorTrims.init(); + TicTacToe.init(); + QuiverWarning.init(); + SpecialEffects.init(); + ItemProtection.init(); + ItemRarityBackgrounds.init(); + containerSolverManager.init(); + statusBarTracker.init(); + Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); + Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100); + Scheduler.INSTANCE.scheduleCyclic(TicTacToe::tick, 4); + Scheduler.INSTANCE.scheduleCyclic(LividColor::update, 10); + Scheduler.INSTANCE.scheduleCyclic(BackpackPreview::tick, 50); + Scheduler.INSTANCE.scheduleCyclic(DwarvenHud::update, 40); + Scheduler.INSTANCE.scheduleCyclic(PlayerListMgr::updateList, 20); + } + + /** + * Ticks the scheduler. Called once at the end of every client tick through + * {@link ClientTickEvents#END_CLIENT_TICK}. + * + * @param client the Minecraft client. + */ + public void tick(MinecraftClient client) { + Scheduler.INSTANCE.tick(); + MessageScheduler.INSTANCE.tick(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/MixinPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/MixinPlugin.java new file mode 100644 index 00000000..c7fc6973 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/MixinPlugin.java @@ -0,0 +1,52 @@ +package de.hysky.skyblocker.compatibility; + +import java.util.List; +import java.util.Set; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import net.fabricmc.loader.api.FabricLoader; + +public class MixinPlugin implements IMixinConfigPlugin { + private static final boolean OPTIFABRIC_LOADED = FabricLoader.getInstance().isModLoaded("optifabric"); + + @Override + public void onLoad(String mixinPackage) { + //Do nothing + } + + @Override + public String getRefMapperConfig() { + return null; + } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + //OptiFabric Compatibility + if (mixinClassName.endsWith("WorldRendererMixin") && OPTIFABRIC_LOADED) return false; + + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { + //Do nothing + } + + @Override + public List getMixins() { + return null; + } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + //Do nothing + } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { + //Do nothing + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java new file mode 100644 index 00000000..5875327d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockEmiRecipe.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.compatibility.emi; + +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import de.hysky.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; +import dev.emi.emi.api.recipe.EmiCraftingRecipe; +import dev.emi.emi.api.recipe.EmiRecipeCategory; +import dev.emi.emi.api.stack.Comparison; +import dev.emi.emi.api.stack.EmiIngredient; +import dev.emi.emi.api.stack.EmiStack; +import dev.emi.emi.api.widget.WidgetHolder; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class SkyblockEmiRecipe extends EmiCraftingRecipe { + private final String craftText; + + public SkyblockEmiRecipe(SkyblockCraftingRecipe recipe) { + super(recipe.getGrid().stream().map(EmiStack::of).map(EmiIngredient.class::cast).toList(), EmiStack.of(recipe.getResult()).comparison(Comparison.compareNbt()), Identifier.of("skyblock", ItemRegistry.getInternalName(recipe.getResult()).toLowerCase().replace(';', '_'))); + this.craftText = recipe.getCraftText(); + } + + @Override + public EmiRecipeCategory getCategory() { + return SkyblockerEMIPlugin.SKYBLOCK; + } + + @Override + public int getDisplayHeight() { + return super.getDisplayHeight() + (craftText.isEmpty() ? 0 : 10); + } + + @Override + public void addWidgets(WidgetHolder widgets) { + super.addWidgets(widgets); + widgets.addText(Text.of(craftText), 59 - MinecraftClient.getInstance().textRenderer.getWidth(craftText) / 2, 55, 0xFFFFFF, true); + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java new file mode 100644 index 00000000..c6147016 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.compatibility.emi; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import de.hysky.skyblocker.utils.ItemUtils; +import dev.emi.emi.api.EmiPlugin; +import dev.emi.emi.api.EmiRegistry; +import dev.emi.emi.api.recipe.EmiRecipeCategory; +import dev.emi.emi.api.render.EmiTexture; +import dev.emi.emi.api.stack.EmiStack; +import net.minecraft.item.Items; +import net.minecraft.util.Identifier; + +/** + * EMI integration + */ +public class SkyblockerEMIPlugin implements EmiPlugin { + public static final Identifier SIMPLIFIED_TEXTURES = new Identifier("emi", "textures/gui/widgets.png"); + // TODO: Custom simplified texture for Skyblock + public static final EmiRecipeCategory SKYBLOCK = new EmiRecipeCategory(new Identifier(SkyblockerMod.NAMESPACE, "skyblock"), EmiStack.of(ItemUtils.getSkyblockerStack()), new EmiTexture(SIMPLIFIED_TEXTURES, 240, 240, 16, 16)); + + @Override + public void register(EmiRegistry registry) { + ItemRegistry.getItemsStream().map(EmiStack::of).forEach(registry::addEmiStack); + registry.addCategory(SKYBLOCK); + registry.addWorkstation(SKYBLOCK, EmiStack.of(Items.CRAFTING_TABLE)); + ItemRegistry.getRecipesStream().map(SkyblockEmiRecipe::new).forEach(registry::addRecipe); + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/modmenu/ModMenuEntry.java b/src/main/java/de/hysky/skyblocker/compatibility/modmenu/ModMenuEntry.java new file mode 100644 index 00000000..e0b0bc2f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/modmenu/ModMenuEntry.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.compatibility.modmenu; + +import com.terraformersmc.modmenu.api.ConfigScreenFactory; +import com.terraformersmc.modmenu.api.ModMenuApi; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; + +@Environment(EnvType.CLIENT) +public class ModMenuEntry implements ModMenuApi { + @Override + public ConfigScreenFactory getModConfigScreenFactory() { + return SkyblockerConfigManager::createGUI; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java new file mode 100644 index 00000000..dfc6e871 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCategory.java @@ -0,0 +1,84 @@ +package de.hysky.skyblocker.compatibility.rei; + +import com.google.common.collect.Lists; +import de.hysky.skyblocker.utils.ItemUtils; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.Renderer; +import me.shedaniel.rei.api.client.gui.widgets.Label; +import me.shedaniel.rei.api.client.gui.widgets.Slot; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.text.Text; + +import java.util.ArrayList; +import java.util.List; + +/** + * Skyblock recipe category class for REI + */ +public class SkyblockCategory implements DisplayCategory { + @Override + public CategoryIdentifier getCategoryIdentifier() { + return SkyblockerREIClientPlugin.SKYBLOCK; + } + + @Override + public Text getTitle() { + return Text.translatable("emi.category.skyblocker.skyblock"); + } + + @Override + public Renderer getIcon() { + return EntryStacks.of(ItemUtils.getSkyblockerStack()); + } + + @Override + public int getDisplayHeight() { + return 73; + } + + /** + * Draws display for SkyblockCraftingDisplay + * + * @param display the display + * @param bounds the bounds of the display, configurable with overriding the width, height methods. + */ + @Override + public List setupDisplay(SkyblockCraftingDisplay display, Rectangle bounds) { + List out = new ArrayList<>(); + out.add(Widgets.createRecipeBase(bounds)); + + Point startPoint; + if (!display.getCraftText().isEmpty() && display.getCraftText() != null) { + startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 31); + } + else { + startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 26); + } + Point resultPoint = new Point(startPoint.x + 95, startPoint.y + 19); + out.add(Widgets.createArrow(new Point(startPoint.x + 60, startPoint.y + 18))); + out.add(Widgets.createResultSlotBackground(resultPoint)); + + // Generate Slots + List input = display.getInputEntries(); + List slots = Lists.newArrayList(); + for (int y = 0; y < 3; y++) + for (int x = 0; x < 3; x++) + slots.add(Widgets.createSlot(new Point(startPoint.x + 1 + x * 18, startPoint.y + 1 + y * 18)).markInput()); + for (int i = 0; i < input.size(); i++) { + slots.get(i).entries(input.get(i)).markInput(); + } + out.addAll(slots); + out.add(Widgets.createSlot(resultPoint).entries(display.getOutputEntries().get(0)).disableBackground().markOutput()); + + // Add craftingText Label + Label craftTextLabel = Widgets.createLabel(new Point(bounds.getCenterX(), startPoint.y + 55), Text.of(display.getCraftText())); + out.add(craftTextLabel); + return out; + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java new file mode 100644 index 00000000..7cd712f2 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.compatibility.rei; + + +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.display.SimpleGridMenuDisplay; +import me.shedaniel.rei.api.common.display.basic.BasicDisplay; +import me.shedaniel.rei.api.common.entry.EntryIngredient; + +import java.util.List; + +/** + * Skyblock Crafting Recipe display class for REI + */ +public class SkyblockCraftingDisplay extends BasicDisplay implements SimpleGridMenuDisplay { + private final String craftText; + + public SkyblockCraftingDisplay(List input, List output, String craftText) { + super(input, output); + this.craftText = craftText; + } + + public String getCraftText() { + return craftText; + } + + @Override + public int getWidth() { + return 3; + } + + @Override + public int getHeight() { + return 3; + } + + @Override + public CategoryIdentifier getCategoryIdentifier() { + return SkyblockerREIClientPlugin.SKYBLOCK; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java new file mode 100644 index 00000000..8db617dc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java @@ -0,0 +1,65 @@ +package de.hysky.skyblocker.compatibility.rei; + +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import de.hysky.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; +import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class SkyblockCraftingDisplayGenerator implements DynamicDisplayGenerator { + + @Override + public Optional> getRecipeFor(EntryStack entry) { + if (!(entry.getValue() instanceof ItemStack)) return Optional.empty(); + EntryStack inputItem = EntryStacks.of((ItemStack) entry.getValue()); + List filteredRecipes = ItemRegistry.getRecipesStream() + .filter(recipe -> ItemRegistry.getInternalName(recipe.getResult()).equals(ItemRegistry.getInternalName(inputItem.getValue()))) + .toList(); + + return Optional.of(generateDisplays(filteredRecipes)); + } + + @Override + public Optional> getUsageFor(EntryStack entry) { + if (!(entry.getValue() instanceof ItemStack)) return Optional.empty(); + EntryStack inputItem = EntryStacks.of((ItemStack) entry.getValue()); + List filteredRecipes = ItemRegistry.getRecipesStream() + .filter(recipe -> { + for (ItemStack item : recipe.getGrid()) { + if(!ItemRegistry.getInternalName(item).isEmpty() && ItemRegistry.getInternalName(item).equals(ItemRegistry.getInternalName(inputItem.getValue()))) + return true; + } + return false; + }) + .toList(); + return Optional.of(generateDisplays(filteredRecipes)); + } + + /** + * Generate Displays from a list of recipes + */ + private List generateDisplays(List recipes) { + List displays = new ArrayList<>(); + for (SkyblockCraftingRecipe recipe : recipes) { + List inputs = new ArrayList<>(); + List outputs = new ArrayList<>(); + + ArrayList> inputEntryStacks = new ArrayList<>(); + recipe.getGrid().forEach((item) -> inputEntryStacks.add(EntryStacks.of(item))); + + for (EntryStack entryStack : inputEntryStacks) { + inputs.add(EntryIngredient.of(entryStack)); + } + outputs.add(EntryIngredient.of(EntryStacks.of(recipe.getResult()))); + + displays.add(new SkyblockCraftingDisplay(inputs, outputs, recipe.getCraftText())); + } + return displays; + } +} diff --git a/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java new file mode 100644 index 00000000..97651718 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java @@ -0,0 +1,34 @@ +package de.hysky.skyblocker.compatibility.rei; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import me.shedaniel.rei.api.client.plugins.REIClientPlugin; +import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; +import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; +import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.util.EntryStacks; +import net.minecraft.item.Items; + +/** + * REI integration + */ +public class SkyblockerREIClientPlugin implements REIClientPlugin { + public static final CategoryIdentifier SKYBLOCK = CategoryIdentifier.of(SkyblockerMod.NAMESPACE, "skyblock"); + + @Override + public void registerCategories(CategoryRegistry categoryRegistry) { + categoryRegistry.addWorkstations(SKYBLOCK, EntryStacks.of(Items.CRAFTING_TABLE)); + categoryRegistry.add(new SkyblockCategory()); + } + + @Override + public void registerDisplays(DisplayRegistry displayRegistry) { + displayRegistry.registerDisplayGenerator(SKYBLOCK, new SkyblockCraftingDisplayGenerator()); + } + + @Override + public void registerEntries(EntryRegistry entryRegistry) { + entryRegistry.addEntries(ItemRegistry.getItemsStream().map(EntryStacks::of).toList()); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/ConfigUtils.java b/src/main/java/de/hysky/skyblocker/config/ConfigUtils.java new file mode 100644 index 00000000..9a7a41b5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/ConfigUtils.java @@ -0,0 +1,25 @@ +package de.hysky.skyblocker.config; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; +import dev.isxander.yacl3.api.controller.EnumControllerBuilder; +import dev.isxander.yacl3.api.controller.ValueFormatter; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.StringUtils; + +import java.util.function.Function; + +public class ConfigUtils { + public static final Function FORMATTING_TO_STRING = formatting -> StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); + public static final ValueFormatter FLOAT_TWO_FORMATTER = value -> Text.literal(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); + + public static BooleanControllerBuilder createBooleanController(Option opt) { + return BooleanControllerBuilder.create(opt).yesNoFormatter().coloured(true); + } + + @SuppressWarnings("unchecked") + public static > EnumControllerBuilder createEnumCyclingListController(Option opt) { + return EnumControllerBuilder.create(opt).enumClass((Class) opt.binding().defaultValue().getClass()); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java new file mode 100644 index 00000000..cb51afdc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java @@ -0,0 +1,787 @@ +package de.hysky.skyblocker.config; + +import dev.isxander.yacl3.config.v2.api.SerialEntry; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import de.hysky.skyblocker.skyblock.item.CustomArmorTrims; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import net.minecraft.client.resource.language.I18n; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.List; + +public class SkyblockerConfig { + @SerialEntry + public int version = 1; + + @SerialEntry + public General general = new General(); + + @SerialEntry + public Locations locations = new Locations(); + + @SerialEntry + public Slayer slayer = new Slayer(); + + @SerialEntry + public QuickNav quickNav = new QuickNav(); + + @SerialEntry + public Messages messages = new Messages(); + + @SerialEntry + public RichPresence richPresence = new RichPresence(); + + 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"); + + @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"); + } + + public static class QuickNavItem { + public QuickNavItem(Boolean render, ItemData itemData, String uiTitle, String clickEvent) { + this.render = render; + this.item = itemData; + this.clickEvent = clickEvent; + this.uiTitle = uiTitle; + } + + @SerialEntry + public Boolean render; + + @SerialEntry + public ItemData item; + + @SerialEntry + public String uiTitle; + + @SerialEntry + public String clickEvent; + } + + public static class ItemData { + public ItemData(String itemName, int count, String nbt) { + this.itemName = itemName; + this.count = count; + this.nbt = nbt; + } + + public ItemData(String itemName) { + this.itemName = itemName; + this.count = 1; + this.nbt = ""; + } + + @SerialEntry + public String itemName; + + @SerialEntry + public int count; + + @SerialEntry + public String nbt; + } + + public static class General { + @SerialEntry + public boolean acceptReparty = true; + + @SerialEntry + public boolean backpackPreviewWithoutShift = false; + + @SerialEntry + public boolean compactorDeletorPreview = true; + + @SerialEntry + public boolean hideEmptyTooltips = true; + + @SerialEntry + public boolean hideStatusEffectOverlay = false; + + @SerialEntry + public TabHudConf tabHud = new TabHudConf(); + + @SerialEntry + public Bars bars = new Bars(); + + @SerialEntry + public Experiments experiments = new Experiments(); + + @SerialEntry + public Fishing fishing = new Fishing(); + + @SerialEntry + public FairySouls fairySouls = new FairySouls(); + + @SerialEntry + public ItemCooldown itemCooldown = new ItemCooldown(); + + @SerialEntry + public Shortcuts shortcuts = new Shortcuts(); + + @SerialEntry + public QuiverWarning quiverWarning = new QuiverWarning(); + + @SerialEntry + public ItemList itemList = new ItemList(); + + @SerialEntry + public ItemTooltip itemTooltip = new ItemTooltip(); + + @SerialEntry + public ItemInfoDisplay itemInfoDisplay = new ItemInfoDisplay(); + + @SerialEntry + public SpecialEffects specialEffects = new SpecialEffects(); + + @SerialEntry + public Hitbox hitbox = new Hitbox(); + + @SerialEntry + public TitleContainer titleContainer = new TitleContainer(); + + @SerialEntry + public TeleportOverlay teleportOverlay = new TeleportOverlay(); + + @SerialEntry + public List lockedSlots = new ArrayList<>(); + + @SerialEntry + public ObjectOpenHashSet protectedItems = new ObjectOpenHashSet<>(); + + @SerialEntry + public Object2ObjectOpenHashMap customItemNames = new Object2ObjectOpenHashMap<>(); + + @SerialEntry + public Object2IntOpenHashMap customDyeColors = new Object2IntOpenHashMap<>(); + + @SerialEntry + public Object2ObjectOpenHashMap customArmorTrims = new Object2ObjectOpenHashMap<>(); + } + + public static class TabHudConf { + @SerialEntry + public boolean tabHudEnabled = true; + + @SerialEntry + public int tabHudScale = 100; + + @SerialEntry + public boolean plainPlayerNames = false; + + @SerialEntry + public NameSorting nameSorting = NameSorting.DEFAULT; + } + + public enum NameSorting { + DEFAULT, ALPHABETICAL; + + @Override + public String toString() { + return switch (this) { + case DEFAULT -> "Default"; + case ALPHABETICAL -> "Alphabetical"; + }; + } + } + + public static class Bars { + @SerialEntry + public boolean enableBars = true; + + @SerialEntry + public BarPositions barPositions = new BarPositions(); + } + + public static class BarPositions { + @SerialEntry + public BarPosition healthBarPosition = BarPosition.LAYER1; + + @SerialEntry + public BarPosition manaBarPosition = BarPosition.LAYER1; + + @SerialEntry + public BarPosition defenceBarPosition = BarPosition.LAYER1; + + @SerialEntry + public BarPosition experienceBarPosition = BarPosition.LAYER1; + + } + + public enum BarPosition { + LAYER1, LAYER2, RIGHT, NONE; + + @Override + public String toString() { + return I18n.translate("text.autoconfig.skyblocker.option.general.bars.barpositions." + name()); + } + + public int toInt() { + return switch (this) { + case LAYER1 -> 0; + case LAYER2 -> 1; + case RIGHT -> 2; + case NONE -> -1; + }; + } + } + + public static class Experiments { + @SerialEntry + public boolean enableChronomatronSolver = true; + + @SerialEntry + public boolean enableSuperpairsSolver = true; + + @SerialEntry + public boolean enableUltrasequencerSolver = true; + } + + public static class Fishing { + @SerialEntry + public boolean enableFishingHelper = true; + } + + public static class FairySouls { + @SerialEntry + public boolean enableFairySoulsHelper = false; + + @SerialEntry + public boolean highlightFoundSouls = true; + + @SerialEntry + public boolean highlightOnlyNearbySouls = false; + } + + public static class ItemCooldown { + @SerialEntry + public boolean enableItemCooldowns = true; + } + + public static class Shortcuts { + @SerialEntry + public boolean enableShortcuts = true; + + @SerialEntry + public boolean enableCommandShortcuts = true; + + @SerialEntry + public boolean enableCommandArgShortcuts = true; + } + + public static class QuiverWarning { + @SerialEntry + public boolean enableQuiverWarning = true; + + @SerialEntry + public boolean enableQuiverWarningInDungeons = true; + + @SerialEntry + public boolean enableQuiverWarningAfterDungeon = true; + } + + public static class Hitbox { + @SerialEntry + public boolean oldFarmlandHitbox = true; + + @SerialEntry + public boolean oldLeverHitbox = false; + } + + public static class TitleContainer { + @SerialEntry + public float titleContainerScale = 100; + + @SerialEntry + public int x = 540; + + @SerialEntry + public int y = 10; + + @SerialEntry + public Direction direction = Direction.HORIZONTAL; + + @SerialEntry + public Alignment alignment = Alignment.MIDDLE; + } + + public static class TeleportOverlay { + @SerialEntry + public boolean enableTeleportOverlays = true; + + @SerialEntry + public boolean enableWeirdTransmission = true; + + @SerialEntry + public boolean enableInstantTransmission = true; + + @SerialEntry + public boolean enableEtherTransmission = true; + + @SerialEntry + public boolean enableSinrecallTransmission = true; + + @SerialEntry + public boolean enableWitherImpact = true; + } + + public enum Direction { + HORIZONTAL, VERTICAL; + + @Override + public String toString() { + return switch (this) { + case HORIZONTAL -> "Horizontal"; + case VERTICAL -> "Vertical"; + }; + } + } + + public enum Alignment { + LEFT, RIGHT, MIDDLE; + + @Override + public String toString() { + return switch (this) { + case LEFT -> "Left"; + case RIGHT -> "Right"; + case MIDDLE -> "Middle"; + }; + } + } + + public static class RichPresence { + @SerialEntry + public boolean enableRichPresence = false; + + @SerialEntry + public Info info = Info.LOCATION; + + @SerialEntry + public boolean cycleMode = false; + + @SerialEntry + public String customMessage = "Playing Skyblock"; + } + + public static class ItemList { + @SerialEntry + public boolean enableItemList = true; + } + + public enum Average { + ONE_DAY, THREE_DAY, BOTH; + + @Override + public String toString() { + return I18n.translate("text.autoconfig.skyblocker.option.general.itemTooltip.avg." + name()); + } + } + + public static class ItemTooltip { + @SerialEntry + public boolean enableNPCPrice = true; + + @SerialEntry + public boolean enableMotesPrice = true; + + @SerialEntry + public boolean enableAvgBIN = true; + + @SerialEntry + public Average avg = Average.THREE_DAY; + + @SerialEntry + public boolean enableLowestBIN = true; + + @SerialEntry + public boolean enableBazaarPrice = true; + + @SerialEntry + public boolean enableMuseumDate = true; + } + + public static class ItemInfoDisplay { + @SerialEntry + public boolean attributeShardInfo = true; + + @SerialEntry + public boolean itemRarityBackgrounds = false; + + @SerialEntry + public float itemRarityBackgroundsOpacity = 1f; + } + + public static class SpecialEffects { + @SerialEntry + public boolean rareDungeonDropEffects = true; + } + + public static class Locations { + @SerialEntry + public Barn barn = new Barn(); + + @SerialEntry + public Dungeons dungeons = new Dungeons(); + + @SerialEntry + public DwarvenMines dwarvenMines = new DwarvenMines(); + + @SerialEntry + public Rift rift = new Rift(); + + @SerialEntry + public SpidersDen spidersDen = new SpidersDen(); + } + + public static class Dungeons { + @SerialEntry + public SecretWaypoints secretWaypoints = new SecretWaypoints(); + + @SerialEntry + public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit(); + + @SerialEntry + public boolean croesusHelper = true; + + @SerialEntry + public boolean enableMap = true; + + @SerialEntry + public float mapScaling = 1f; + + @SerialEntry + public int mapX = 2; + + @SerialEntry + public int mapY = 2; + + @SerialEntry + public boolean starredMobGlow = true; + + @SerialEntry + public boolean solveThreeWeirdos = true; + + @SerialEntry + public boolean blazesolver = true; + + @SerialEntry + public boolean solveTrivia = true; + + @SerialEntry + public boolean solveTicTacToe = true; + + @SerialEntry + public LividColor lividColor = new LividColor(); + + @SerialEntry + public Terminals terminals = new Terminals(); + } + + public static class SecretWaypoints { + @SerialEntry + public boolean enableSecretWaypoints = true; + + @SerialEntry + public boolean noInitSecretWaypoints = false; + + @SerialEntry + public boolean enableEntranceWaypoints = true; + + @SerialEntry + public boolean enableSuperboomWaypoints = true; + + @SerialEntry + public boolean enableChestWaypoints = true; + + @SerialEntry + public boolean enableItemWaypoints = true; + + @SerialEntry + public boolean enableBatWaypoints = true; + + @SerialEntry + public boolean enableWitherWaypoints = true; + + @SerialEntry + public boolean enableLeverWaypoints = true; + + @SerialEntry + public boolean enableFairySoulWaypoints = true; + + @SerialEntry + public boolean enableStonkWaypoints = true; + + @SerialEntry + public boolean enableDefaultWaypoints = true; + } + + public static class DungeonChestProfit { + @SerialEntry + public boolean enableProfitCalculator = true; + + @SerialEntry + public boolean includeKismet = false; + + @SerialEntry + public boolean includeEssence = true; + + @SerialEntry + public int neutralThreshold = 1000; + + @SerialEntry + public Formatting neutralColor = Formatting.DARK_GRAY; + + @SerialEntry + public Formatting profitColor = Formatting.DARK_GREEN; + + @SerialEntry + public Formatting lossColor = Formatting.RED; + + @SerialEntry + public Formatting incompleteColor = Formatting.BLUE; + + } + + public static class LividColor { + @SerialEntry + public boolean enableLividColor = true; + + @SerialEntry + public String lividColorText = "The livid color is [color]"; + } + + public static class Terminals { + @SerialEntry + public boolean solveColor = true; + + @SerialEntry + public boolean solveOrder = true; + + @SerialEntry + public boolean solveStartsWith = true; + } + + public static class DwarvenMines { + @SerialEntry + public boolean enableDrillFuel = true; + + @SerialEntry + public boolean solveFetchur = true; + + @SerialEntry + public boolean solvePuzzler = true; + + @SerialEntry + public DwarvenHud dwarvenHud = new DwarvenHud(); + } + + public static class DwarvenHud { + @SerialEntry + public boolean enabled = true; + + @SerialEntry + public DwarvenHudStyle style = DwarvenHudStyle.SIMPLE; + + @SerialEntry + public boolean enableBackground = true; + + @SerialEntry + public int x = 10; + + @SerialEntry + public int y = 10; + } + + public enum DwarvenHudStyle { + SIMPLE, FANCY, CLASSIC; + + @Override + public String toString() { + return switch (this) { + case SIMPLE -> "Simple"; + case FANCY -> "Fancy"; + case CLASSIC -> "Classic"; + }; + } + } + + public static class Barn { + @SerialEntry + public boolean solveHungryHiker = true; + + @SerialEntry + public boolean solveTreasureHunter = true; + } + + public static class Rift { + @SerialEntry + public boolean mirrorverseWaypoints = true; + + @SerialEntry + public int mcGrubberStacks = 0; + } + + public static class SpidersDen { + @SerialEntry + public Relics relics = new Relics(); + } + + public static class Relics { + @SerialEntry + public boolean enableRelicsHelper = false; + + @SerialEntry + public boolean highlightFoundRelics = true; + } + + public static class Slayer { + @SerialEntry + public VampireSlayer vampireSlayer = new VampireSlayer(); + } + + public static class VampireSlayer { + @SerialEntry + public boolean enableEffigyWaypoints = true; + + @SerialEntry + public boolean compactEffigyWaypoints; + + @SerialEntry + public int effigyUpdateFrequency = 5; + + @SerialEntry + public boolean enableHolyIceIndicator = true; + + @SerialEntry + public int holyIceIndicatorTickDelay = 10; + + @SerialEntry + public int holyIceUpdateFrequency = 5; + + @SerialEntry + public boolean enableHealingMelonIndicator = true; + + @SerialEntry + public float healingMelonHealthThreshold = 4f; + + @SerialEntry + public boolean enableSteakStakeIndicator = true; + + @SerialEntry + public int steakStakeUpdateFrequency = 5; + + @SerialEntry + public boolean enableManiaIndicator = true; + + @SerialEntry + public int maniaUpdateFrequency = 5; + } + + public static class Messages { + @SerialEntry + public ChatFilterResult hideAbility = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideHeal = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideAOTE = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideImplosion = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideMoltenWave = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideAds = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideTeleportPad = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideCombo = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideAutopet = ChatFilterResult.PASS; + + @SerialEntry + public ChatFilterResult hideShowOff = ChatFilterResult.PASS; + + @SerialEntry + public boolean hideMana = false; + } + + public enum Info { + PURSE, BITS, LOCATION; + + @Override + public String toString() { + return I18n.translate("text.autoconfig.skyblocker.option.richPresence.info." + name()); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java new file mode 100644 index 00000000..98c83975 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfigManager.java @@ -0,0 +1,86 @@ +package de.hysky.skyblocker.config; + +import java.lang.StackWalker.Option; +import java.nio.file.Path; + +import com.google.gson.FieldNamingPolicy; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; + +import de.hysky.skyblocker.SkyblockerMod; +import dev.isxander.yacl3.api.YetAnotherConfigLib; +import dev.isxander.yacl3.config.v2.api.ConfigClassHandler; +import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder; +import de.hysky.skyblocker.config.categories.DiscordRPCCategory; +import de.hysky.skyblocker.config.categories.DungeonsCategory; +import de.hysky.skyblocker.config.categories.DwarvenMinesCategory; +import de.hysky.skyblocker.config.categories.GeneralCategory; +import de.hysky.skyblocker.config.categories.LocationsCategory; +import de.hysky.skyblocker.config.categories.MessageFilterCategory; +import de.hysky.skyblocker.config.categories.QuickNavigationCategory; +import de.hysky.skyblocker.config.categories.SlayersCategory; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class SkyblockerConfigManager { + private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve("skyblocker.json"); + private static final ConfigClassHandler HANDLER = ConfigClassHandler.createBuilder(SkyblockerConfig.class) + .serializer(config -> GsonConfigSerializerBuilder.create(config) + .setPath(PATH) + .setJson5(false) + .appendGsonBuilder(builder -> builder + .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) + .registerTypeHierarchyAdapter(Identifier.class, new Identifier.Serializer())) + .build()) + .build(); + + public static SkyblockerConfig get() { + return HANDLER.instance(); + } + + /** + * This method is caller sensitive and can only be called by the mod initializer, + * this is enforced. + */ + public static void init() { + if (StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).getCallerClass() != SkyblockerMod.class) { + throw new RuntimeException("Skyblocker: Called config init from an illegal place!"); + } + + HANDLER.load(); + ClientCommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE).then(optionsLiteral("config")).then(optionsLiteral("options"))))); + } + + public static void save() { + HANDLER.save(); + } + + public static Screen createGUI(Screen parent) { + return YetAnotherConfigLib.create(HANDLER, (defaults, config, builder) -> builder + .title(Text.translatable("text.autoconfig.skyblocker.title")) + .category(GeneralCategory.create(defaults, config)) + .category(DungeonsCategory.create(defaults, config)) + .category(DwarvenMinesCategory.create(defaults, config)) + .category(LocationsCategory.create(defaults, config)) + .category(SlayersCategory.create(defaults, config)) + .category(QuickNavigationCategory.create(defaults, config)) + .category(MessageFilterCategory.create(defaults, config)) + .category(DiscordRPCCategory.create(defaults, config))).generateScreen(parent); + } + + /** + * Registers an options command with the given name. Used for registering both options and config as valid commands. + * + * @param name the name of the command node + * @return the command builder + */ + private static LiteralArgumentBuilder optionsLiteral(String name) { + // Don't immediately open the next screen as it will be closed by ChatScreen right after this command is executed + return ClientCommandManager.literal(name).executes(Scheduler.queueOpenScreenCommand(() -> createGUI(null))); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DiscordRPCCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DiscordRPCCategory.java new file mode 100644 index 00000000..fcdc3d8d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/DiscordRPCCategory.java @@ -0,0 +1,49 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import dev.isxander.yacl3.api.ConfigCategory; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.api.controller.StringControllerBuilder; +import net.minecraft.text.Text; + +public class DiscordRPCCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.richPresence")) + + //Uncategorized Options + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.enableRichPresence")) + .binding(defaults.richPresence.enableRichPresence, + () -> config.richPresence.enableRichPresence, + newValue -> config.richPresence.enableRichPresence = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.info")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.richPresence.info.@Tooltip"))) + .binding(defaults.richPresence.info, + () -> config.richPresence.info, + newValue -> config.richPresence.info = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.cycleMode")) + .binding(defaults.richPresence.cycleMode, + () -> config.richPresence.cycleMode, + newValue -> config.richPresence.cycleMode = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.customMessage")) + .binding(defaults.richPresence.customMessage, + () -> config.richPresence.customMessage, + newValue -> config.richPresence.customMessage = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java new file mode 100644 index 00000000..ffd979eb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java @@ -0,0 +1,316 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import dev.isxander.yacl3.api.ButtonOption; +import dev.isxander.yacl3.api.ConfigCategory; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import dev.isxander.yacl3.api.OptionFlag; +import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.StringControllerBuilder; +import de.hysky.skyblocker.config.controllers.EnumDropdownControllerBuilder; +import de.hysky.skyblocker.skyblock.dungeon.DungeonMapConfigScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class DungeonsCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons")) + + //Dungeon Secret Waypoints + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSecretWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableSecretWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableSecretWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableSecretWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.noInitSecretWaypoints")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.noInitSecretWaypoints.@Tooltip"))) + .binding(defaults.locations.dungeons.secretWaypoints.noInitSecretWaypoints, + () -> config.locations.dungeons.secretWaypoints.noInitSecretWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.noInitSecretWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .flag(OptionFlag.GAME_RESTART) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableEntranceWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableEntranceWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableEntranceWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableEntranceWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSuperboomWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableSuperboomWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableSuperboomWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableSuperboomWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableChestWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableChestWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableChestWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableChestWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableItemWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableItemWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableItemWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableItemWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableBatWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableBatWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableBatWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableBatWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableWitherWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableWitherWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableWitherWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableWitherWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableLeverWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableLeverWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableLeverWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableLeverWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableFairySoulWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableFairySoulWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableFairySoulWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableFairySoulWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableStonkWaypoints")) + .binding(defaults.locations.dungeons.secretWaypoints.enableStonkWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableStonkWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableStonkWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints.@Tooltip"))) + .binding(defaults.locations.dungeons.secretWaypoints.enableDefaultWaypoints, + () -> config.locations.dungeons.secretWaypoints.enableDefaultWaypoints, + newValue -> config.locations.dungeons.secretWaypoints.enableDefaultWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator.@Tooltip"))) + .binding(defaults.locations.dungeons.dungeonChestProfit.enableProfitCalculator, + () -> config.locations.dungeons.dungeonChestProfit.enableProfitCalculator, + newValue -> config.locations.dungeons.dungeonChestProfit.enableProfitCalculator = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet.@Tooltip"))) + .binding(defaults.locations.dungeons.dungeonChestProfit.includeKismet, + () -> config.locations.dungeons.dungeonChestProfit.includeKismet, + newValue -> config.locations.dungeons.dungeonChestProfit.includeKismet = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence.@Tooltip"))) + .binding(defaults.locations.dungeons.dungeonChestProfit.includeEssence, + () -> config.locations.dungeons.dungeonChestProfit.includeEssence, + newValue -> config.locations.dungeons.dungeonChestProfit.includeEssence = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold.@Tooltip"))) + .binding(defaults.locations.dungeons.dungeonChestProfit.neutralThreshold, + () -> config.locations.dungeons.dungeonChestProfit.neutralThreshold, + newValue -> config.locations.dungeons.dungeonChestProfit.neutralThreshold = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralColor")) + .binding(defaults.locations.dungeons.dungeonChestProfit.neutralColor, + () -> config.locations.dungeons.dungeonChestProfit.neutralColor, + newValue -> config.locations.dungeons.dungeonChestProfit.neutralColor = newValue) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.profitColor")) + .binding(defaults.locations.dungeons.dungeonChestProfit.profitColor, + () -> config.locations.dungeons.dungeonChestProfit.profitColor, + newValue -> config.locations.dungeons.dungeonChestProfit.profitColor = newValue) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor")) + .binding(defaults.locations.dungeons.dungeonChestProfit.lossColor, + () -> config.locations.dungeons.dungeonChestProfit.lossColor, + newValue -> config.locations.dungeons.dungeonChestProfit.lossColor = newValue) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor.@Tooltip"))) + .binding(defaults.locations.dungeons.dungeonChestProfit.incompleteColor, + () -> config.locations.dungeons.dungeonChestProfit.incompleteColor, + newValue -> config.locations.dungeons.dungeonChestProfit.incompleteColor = newValue) + .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) + .build()) + .build()) + + //Others + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip"))) + .binding(defaults.locations.dungeons.croesusHelper, + () -> config.locations.dungeons.croesusHelper, + newValue -> config.locations.dungeons.croesusHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.enableMap")) + .binding(defaults.locations.dungeons.enableMap, + () -> config.locations.dungeons.enableMap, + newValue -> config.locations.dungeons.enableMap = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(ButtonOption.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapScreen")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new DungeonMapConfigScreen(screen))) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapScaling")) + .binding(defaults.locations.dungeons.mapScaling, + () -> config.locations.dungeons.mapScaling, + newValue -> config.locations.dungeons.mapScaling = newValue) + .controller(FloatFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapX")) + .binding(defaults.locations.dungeons.mapX, + () -> config.locations.dungeons.mapX, + newValue -> config.locations.dungeons.mapX = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapY")) + .binding(defaults.locations.dungeons.mapY, + () -> config.locations.dungeons.mapY, + newValue -> config.locations.dungeons.mapY = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow.@Tooltip"))) + .binding(defaults.locations.dungeons.starredMobGlow, + () -> config.locations.dungeons.starredMobGlow, + newValue -> config.locations.dungeons.starredMobGlow = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos")) + .binding(defaults.locations.dungeons.solveThreeWeirdos, + () -> config.locations.dungeons.solveThreeWeirdos, + newValue -> config.locations.dungeons.solveThreeWeirdos = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazesolver")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazesolver.@Tooltip"))) + .binding(defaults.locations.dungeons.blazesolver, + () -> config.locations.dungeons.blazesolver, + newValue -> config.locations.dungeons.blazesolver = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia")) + .binding(defaults.locations.dungeons.solveTrivia, + () -> config.locations.dungeons.solveTrivia, + newValue -> config.locations.dungeons.solveTrivia = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe.@Tooltip"))) + .binding(defaults.locations.dungeons.solveTicTacToe, + () -> config.locations.dungeons.solveTicTacToe, + newValue -> config.locations.dungeons.solveTicTacToe = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + + //Livid Color + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor.@Tooltip"))) + .binding(defaults.locations.dungeons.lividColor.enableLividColor, + () -> config.locations.dungeons.lividColor.enableLividColor, + newValue -> config.locations.dungeons.lividColor.enableLividColor = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip"))) + .binding(defaults.locations.dungeons.lividColor.lividColorText, + () -> config.locations.dungeons.lividColor.lividColorText, + newValue -> config.locations.dungeons.lividColor.lividColorText = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Terminal Solvers + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor")) + .binding(defaults.locations.dungeons.terminals.solveColor, + () -> config.locations.dungeons.terminals.solveColor, + newValue -> config.locations.dungeons.terminals.solveColor = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder")) + .binding(defaults.locations.dungeons.terminals.solveOrder, + () -> config.locations.dungeons.terminals.solveOrder, + newValue -> config.locations.dungeons.terminals.solveOrder = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith")) + .binding(defaults.locations.dungeons.terminals.solveStartsWith, + () -> config.locations.dungeons.terminals.solveStartsWith, + newValue -> config.locations.dungeons.terminals.solveStartsWith = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java new file mode 100644 index 00000000..35c91d64 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/DwarvenMinesCategory.java @@ -0,0 +1,94 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import dev.isxander.yacl3.api.ButtonOption; +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 de.hysky.skyblocker.skyblock.dwarven.DwarvenHudConfigScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +public class DwarvenMinesCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines")) + + //Uncategorized Options + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel")) + .binding(defaults.locations.dwarvenMines.enableDrillFuel, + () -> config.locations.dwarvenMines.enableDrillFuel, + newValue -> config.locations.dwarvenMines.enableDrillFuel = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur")) + .binding(defaults.locations.dwarvenMines.solveFetchur, + () -> config.locations.dwarvenMines.solveFetchur, + newValue -> config.locations.dwarvenMines.solveFetchur = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler")) + .binding(defaults.locations.dwarvenMines.solvePuzzler, + () -> config.locations.dwarvenMines.solvePuzzler, + newValue -> config.locations.dwarvenMines.solvePuzzler = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + + //Dwarven HUD + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud")) + .collapsed(false) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled")) + .binding(defaults.locations.dwarvenMines.dwarvenHud.enabled, + () -> config.locations.dwarvenMines.dwarvenHud.enabled, + newValue -> config.locations.dwarvenMines.dwarvenHud.enabled = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]"), + Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]"), + Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]"))) + .binding(defaults.locations.dwarvenMines.dwarvenHud.style, + () -> config.locations.dwarvenMines.dwarvenHud.style, + newValue -> config.locations.dwarvenMines.dwarvenHud.style = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(ButtonOption.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.screen")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new DwarvenHudConfigScreen(screen))) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground")) + .binding(defaults.locations.dwarvenMines.dwarvenHud.enableBackground, + () -> config.locations.dwarvenMines.dwarvenHud.enableBackground, + newValue -> config.locations.dwarvenMines.dwarvenHud.enableBackground = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.x")) + .binding(defaults.locations.dwarvenMines.dwarvenHud.x, + () -> config.locations.dwarvenMines.dwarvenHud.x, + newValue -> config.locations.dwarvenMines.dwarvenHud.x = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.y")) + .binding(defaults.locations.dwarvenMines.dwarvenHud.y, + () -> config.locations.dwarvenMines.dwarvenHud.y, + newValue -> config.locations.dwarvenMines.dwarvenHud.y = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .build()) + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java new file mode 100644 index 00000000..6a393868 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java @@ -0,0 +1,508 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import dev.isxander.yacl3.api.*; +import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; +import de.hysky.skyblocker.skyblock.shortcut.ShortcutsConfigScreen; +import de.hysky.skyblocker.utils.render.title.TitleContainerConfigScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +public class GeneralCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.general")) + + //Ungrouped Options + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.acceptReparty")) + .binding(defaults.general.acceptReparty, + () -> config.general.acceptReparty, + newValue -> config.general.acceptReparty = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift")) + .binding(defaults.general.backpackPreviewWithoutShift, + () -> config.general.backpackPreviewWithoutShift, + newValue -> config.general.backpackPreviewWithoutShift = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.compactorDeletorPreview")) + .binding(defaults.general.compactorDeletorPreview, + () -> config.general.compactorDeletorPreview, + newValue -> config.general.compactorDeletorPreview = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.hideEmptyTooltips")) + .binding(defaults.general.hideEmptyTooltips, + () -> config.general.hideEmptyTooltips, + newValue -> config.general.hideEmptyTooltips = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.hideStatusEffectOverlay")) + .binding(defaults.general.hideStatusEffectOverlay, + () -> config.general.hideStatusEffectOverlay, + newValue -> config.general.hideStatusEffectOverlay = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + + //Tab Hud + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled")) + .binding(defaults.general.tabHud.tabHudEnabled, + () -> config.general.tabHud.tabHudEnabled, + newValue -> config.general.tabHud.tabHudEnabled = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.tabHudScale")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip"))) + .binding(defaults.general.tabHud.tabHudScale, + () -> config.general.tabHud.tabHudScale, + newValue -> config.general.tabHud.tabHudScale = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(10, 200).step(1)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames.@Tooltip"))) + .binding(defaults.general.tabHud.plainPlayerNames, + () -> config.general.tabHud.plainPlayerNames, + newValue -> config.general.tabHud.plainPlayerNames = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.nameSorting")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.nameSorting.@Tooltip"))) + .binding(defaults.general.tabHud.nameSorting, + () -> config.general.tabHud.nameSorting, + newValue -> config.general.tabHud.nameSorting = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .build()) + + //Fancy Bars + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.enableBars")) + .binding(defaults.general.bars.enableBars, + () -> config.general.bars.enableBars, + newValue -> config.general.bars.enableBars = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition")) + .binding(defaults.general.bars.barPositions.healthBarPosition, + () -> config.general.bars.barPositions.healthBarPosition, + newValue -> config.general.bars.barPositions.healthBarPosition = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition")) + .binding(defaults.general.bars.barPositions.manaBarPosition, + () -> config.general.bars.barPositions.manaBarPosition, + newValue -> config.general.bars.barPositions.manaBarPosition = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition")) + .binding(defaults.general.bars.barPositions.defenceBarPosition, + () -> config.general.bars.barPositions.defenceBarPosition, + newValue -> config.general.bars.barPositions.defenceBarPosition = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition")) + .binding(defaults.general.bars.barPositions.experienceBarPosition, + () -> config.general.bars.barPositions.experienceBarPosition, + newValue -> config.general.bars.barPositions.experienceBarPosition = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .build()) + + //Experiments Solver + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments.enableChronomatronSolver")) + .binding(defaults.general.experiments.enableChronomatronSolver, + () -> config.general.experiments.enableChronomatronSolver, + newValue -> config.general.experiments.enableChronomatronSolver = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments.enableSuperpairsSolver")) + .binding(defaults.general.experiments.enableSuperpairsSolver, + () -> config.general.experiments.enableSuperpairsSolver, + newValue -> config.general.experiments.enableSuperpairsSolver = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments.enableUltrasequencerSolver")) + .binding(defaults.general.experiments.enableUltrasequencerSolver, + () -> config.general.experiments.enableUltrasequencerSolver, + newValue -> config.general.experiments.enableUltrasequencerSolver = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Fishing Helper + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fishing")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper")) + .binding(defaults.general.fishing.enableFishingHelper, + () -> config.general.fishing.enableFishingHelper, + newValue -> config.general.fishing.enableFishingHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Fairy Souls Helper + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.enableFairySoulsHelper")) + .binding(defaults.general.fairySouls.enableFairySoulsHelper, + () -> config.general.fairySouls.enableFairySoulsHelper, + newValue -> config.general.fairySouls.enableFairySoulsHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.highlightFoundSouls")) + .binding(defaults.general.fairySouls.highlightFoundSouls, + () -> config.general.fairySouls.highlightFoundSouls, + newValue -> config.general.fairySouls.highlightFoundSouls = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls.@Tooltip"))) + .binding(defaults.general.fairySouls.highlightOnlyNearbySouls, + () -> config.general.fairySouls.highlightOnlyNearbySouls, + newValue -> config.general.fairySouls.highlightOnlyNearbySouls = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Item Cooldown + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown.enableItemCooldowns")) + .binding(defaults.general.itemCooldown.enableItemCooldowns, + () -> config.general.itemCooldown.enableItemCooldowns, + newValue -> config.general.itemCooldown.enableItemCooldowns = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Shortcuts + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip"))) + .binding(defaults.general.shortcuts.enableShortcuts, + () -> config.general.shortcuts.enableShortcuts, + newValue -> config.general.shortcuts.enableShortcuts = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts.@Tooltip"))) + .binding(defaults.general.shortcuts.enableCommandShortcuts, + () -> config.general.shortcuts.enableCommandShortcuts, + newValue -> config.general.shortcuts.enableCommandShortcuts = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts.@Tooltip"))) + .binding(defaults.general.shortcuts.enableCommandArgShortcuts, + () -> config.general.shortcuts.enableCommandArgShortcuts, + newValue -> config.general.shortcuts.enableCommandArgShortcuts = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(ButtonOption.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.config")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new ShortcutsConfigScreen(screen))) + .build()) + .build()) + + //Quiver Warning + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarning")) + .binding(defaults.general.quiverWarning.enableQuiverWarning, + () -> config.general.quiverWarning.enableQuiverWarning, + newValue -> config.general.quiverWarning.enableQuiverWarning = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningInDungeons")) + .binding(defaults.general.quiverWarning.enableQuiverWarningInDungeons, + () -> config.general.quiverWarning.enableQuiverWarningInDungeons, + newValue -> config.general.quiverWarning.enableQuiverWarningInDungeons = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningAfterDungeon")) + .binding(defaults.general.quiverWarning.enableQuiverWarningAfterDungeon, + () -> config.general.quiverWarning.enableQuiverWarningAfterDungeon, + newValue -> config.general.quiverWarning.enableQuiverWarningAfterDungeon = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Item List + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemList")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemList.enableItemList")) + .binding(defaults.general.itemList.enableItemList, + () -> config.general.itemList.enableItemList, + newValue -> config.general.itemList.enableItemList = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Item Tooltip + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice")) + .binding(defaults.general.itemTooltip.enableNPCPrice, + () -> config.general.itemTooltip.enableNPCPrice, + newValue -> config.general.itemTooltip.enableNPCPrice = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice.@Tooltip"))) + .binding(defaults.general.itemTooltip.enableMotesPrice, + () -> config.general.itemTooltip.enableMotesPrice, + newValue -> config.general.itemTooltip.enableMotesPrice = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN")) + .binding(defaults.general.itemTooltip.enableAvgBIN, + () -> config.general.itemTooltip.enableAvgBIN, + newValue -> config.general.itemTooltip.enableAvgBIN = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.avg")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip"))) + .binding(defaults.general.itemTooltip.avg, + () -> config.general.itemTooltip.avg, + newValue -> config.general.itemTooltip.avg = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN")) + .binding(defaults.general.itemTooltip.enableLowestBIN, + () -> config.general.itemTooltip.enableLowestBIN, + newValue -> config.general.itemTooltip.enableLowestBIN = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice")) + .binding(defaults.general.itemTooltip.enableBazaarPrice, + () -> config.general.itemTooltip.enableBazaarPrice, + newValue -> config.general.itemTooltip.enableBazaarPrice = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate")) + .binding(defaults.general.itemTooltip.enableMuseumDate, + () -> config.general.itemTooltip.enableMuseumDate, + newValue -> config.general.itemTooltip.enableMuseumDate = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Item Info Display + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo.@Tooltip"))) + .binding(defaults.general.itemInfoDisplay.attributeShardInfo, + () -> config.general.itemInfoDisplay.attributeShardInfo, + newValue -> config.general.itemInfoDisplay.attributeShardInfo = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip"))) + .binding(defaults.general.itemInfoDisplay.itemRarityBackgrounds, + () -> config.general.itemInfoDisplay.itemRarityBackgrounds, + newValue -> config.general.itemInfoDisplay.itemRarityBackgrounds = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundsOpacity")) + .binding(defaults.general.itemInfoDisplay.itemRarityBackgroundsOpacity, + () -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity, + newValue -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity = newValue) + .controller(opt -> FloatSliderControllerBuilder.create(opt).range(0f, 1f).step(0.05f).formatValue(ConfigUtils.FLOAT_TWO_FORMATTER)) + .build()) + .build()) + + //Special Effects + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects.@Tooltip"))) + .binding(defaults.general.specialEffects.rareDungeonDropEffects, + () -> config.general.specialEffects.rareDungeonDropEffects, + newValue -> config.general.specialEffects.rareDungeonDropEffects = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Hitboxes + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox")) + .binding(defaults.general.hitbox.oldFarmlandHitbox, + () -> config.general.hitbox.oldFarmlandHitbox, + newValue -> config.general.hitbox.oldFarmlandHitbox = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox")) + .binding(defaults.general.hitbox.oldLeverHitbox, + () -> config.general.hitbox.oldLeverHitbox, + newValue -> config.general.hitbox.oldLeverHitbox = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //Title Container + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.@Tooltip"))) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.titleContainerScale")) + .binding(defaults.general.titleContainer.titleContainerScale, + () -> config.general.titleContainer.titleContainerScale, + newValue -> config.general.titleContainer.titleContainerScale = newValue) + .controller(opt -> FloatFieldControllerBuilder.create(opt).range(30f, 140f)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.x")) + .binding(defaults.general.titleContainer.x, + () -> config.general.titleContainer.x, + newValue -> config.general.titleContainer.x = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.y")) + .binding(defaults.general.titleContainer.y, + () -> config.general.titleContainer.y, + newValue -> config.general.titleContainer.y = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.direction")) + .binding(defaults.general.titleContainer.direction, + () -> config.general.titleContainer.direction, + newValue -> config.general.titleContainer.direction = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.alignment")) + .binding(defaults.general.titleContainer.alignment, + () -> config.general.titleContainer.alignment, + newValue -> config.general.titleContainer.alignment = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(ButtonOption.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.config")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new TitleContainerConfigScreen(screen))) + .build()) + .build()) + + //Teleport Overlays + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableTeleportOverlays")) + .binding(defaults.general.teleportOverlay.enableTeleportOverlays, + () -> config.general.teleportOverlay.enableTeleportOverlays, + newValue -> config.general.teleportOverlay.enableTeleportOverlays = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableWeirdTransmission")) + .binding(defaults.general.teleportOverlay.enableWeirdTransmission, + () -> config.general.teleportOverlay.enableWeirdTransmission, + newValue -> config.general.teleportOverlay.enableWeirdTransmission = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableInstantTransmission")) + .binding(defaults.general.teleportOverlay.enableInstantTransmission, + () -> config.general.teleportOverlay.enableInstantTransmission, + newValue -> config.general.teleportOverlay.enableInstantTransmission = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableEtherTransmission")) + .binding(defaults.general.teleportOverlay.enableEtherTransmission, + () -> config.general.teleportOverlay.enableEtherTransmission, + newValue -> config.general.teleportOverlay.enableEtherTransmission = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableSinrecallTransmission")) + .binding(defaults.general.teleportOverlay.enableSinrecallTransmission, + () -> config.general.teleportOverlay.enableSinrecallTransmission, + newValue -> config.general.teleportOverlay.enableSinrecallTransmission = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableWitherImpact")) + .binding(defaults.general.teleportOverlay.enableWitherImpact, + () -> config.general.teleportOverlay.enableWitherImpact, + newValue -> config.general.teleportOverlay.enableWitherImpact = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java new file mode 100644 index 00000000..399bb9f6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/LocationsCategory.java @@ -0,0 +1,80 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +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.IntegerSliderControllerBuilder; +import net.minecraft.text.Text; + +public class LocationsCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.locations")) + + //Barn + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.barn")) + .collapsed(false) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker")) + .binding(defaults.locations.barn.solveHungryHiker, + () -> config.locations.barn.solveHungryHiker, + newValue -> config.locations.barn.solveHungryHiker = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter")) + .binding(defaults.locations.barn.solveTreasureHunter, + () -> config.locations.barn.solveTreasureHunter, + newValue -> config.locations.barn.solveTreasureHunter = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + + //The Rift + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.rift")) + .collapsed(false) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.rift.mirrorverseWaypoints")) + .binding(defaults.locations.rift.mirrorverseWaypoints, + () -> config.locations.rift.mirrorverseWaypoints, + newValue -> config.locations.rift.mirrorverseWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.rift.mcGrubberStacks")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.rift.mcGrubberStacks.@Tooltip"))) + .binding(defaults.locations.rift.mcGrubberStacks, + () -> config.locations.rift.mcGrubberStacks, + newValue -> config.locations.rift.mcGrubberStacks = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(0, 5).step(1)) + .build()) + .build()) + + //Spider's Den + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.spidersDen")) + .collapsed(false) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.spidersDen.relics.enableRelicsHelper")) + .binding(defaults.locations.spidersDen.relics.enableRelicsHelper, + () -> config.locations.spidersDen.relics.enableRelicsHelper, + newValue -> config.locations.spidersDen.relics.enableRelicsHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.locations.spidersDen.relics.highlightFoundRelics")) + .binding(defaults.locations.spidersDen.relics.highlightFoundRelics, + () -> config.locations.spidersDen.relics.highlightFoundRelics, + newValue -> config.locations.spidersDen.relics.highlightFoundRelics = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .build()) + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java new file mode 100644 index 00000000..ba76a903 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/MessageFilterCategory.java @@ -0,0 +1,98 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import dev.isxander.yacl3.api.ConfigCategory; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionDescription; +import net.minecraft.text.Text; + +public class MessageFilterCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.messages")) + + //Uncategorized Options + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAbility")) + .binding(defaults.messages.hideAbility, + () -> config.messages.hideAbility, + newValue -> config.messages.hideAbility = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideHeal")) + .binding(defaults.messages.hideHeal, + () -> config.messages.hideHeal, + newValue -> config.messages.hideHeal = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAOTE")) + .binding(defaults.messages.hideAOTE, + () -> config.messages.hideAOTE, + newValue -> config.messages.hideAOTE = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideImplosion")) + .binding(defaults.messages.hideImplosion, + () -> config.messages.hideImplosion, + newValue -> config.messages.hideImplosion = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideMoltenWave")) + .binding(defaults.messages.hideMoltenWave, + () -> config.messages.hideMoltenWave, + newValue -> config.messages.hideMoltenWave = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAds")) + .binding(defaults.messages.hideAds, + () -> config.messages.hideAds, + newValue -> config.messages.hideAds = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideTeleportPad")) + .binding(defaults.messages.hideTeleportPad, + () -> config.messages.hideTeleportPad, + newValue -> config.messages.hideTeleportPad = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideCombo")) + .binding(defaults.messages.hideCombo, + () -> config.messages.hideCombo, + newValue -> config.messages.hideCombo = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAutopet")) + .binding(defaults.messages.hideAutopet, + () -> config.messages.hideAutopet, + newValue -> config.messages.hideAutopet = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideShowOff")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.hideShowOff.@Tooltip"))) + .binding(defaults.messages.hideShowOff, + () -> config.messages.hideShowOff, + newValue -> config.messages.hideShowOff = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideMana")) + .binding(defaults.messages.hideMana, + () -> config.messages.hideMana, + newValue -> config.messages.hideMana = newValue) + .controller(ConfigUtils::createBooleanController) + .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 new file mode 100644 index 00000000..b17fed23 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/QuickNavigationCategory.java @@ -0,0 +1,605 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +import dev.isxander.yacl3.api.ConfigCategory; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.OptionGroup; +import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.StringControllerBuilder; +import net.minecraft.text.Text; + +public class QuickNavigationCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.quickNav")) + + //Toggle + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.enableQuickNav")) + .binding(defaults.quickNav.enableQuickNav, + () -> config.quickNav.enableQuickNav, + newValue -> config.quickNav.enableQuickNav = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + + //Button 1 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button1.render, + () -> config.quickNav.button1.render, + newValue -> config.quickNav.button1.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button1.item.itemName, + () -> config.quickNav.button1.item.itemName, + newValue -> config.quickNav.button1.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button1.item.nbt, + () -> config.quickNav.button1.item.nbt, + newValue -> config.quickNav.button1.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button1.uiTitle, + () -> config.quickNav.button1.uiTitle, + newValue -> config.quickNav.button1.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button2")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button2.render, + () -> config.quickNav.button2.render, + newValue -> config.quickNav.button2.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button2.item.itemName, + () -> config.quickNav.button2.item.itemName, + newValue -> config.quickNav.button2.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button2.item.nbt, + () -> config.quickNav.button2.item.nbt, + newValue -> config.quickNav.button2.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button2.uiTitle, + () -> config.quickNav.button2.uiTitle, + newValue -> config.quickNav.button2.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button3")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button3.render, + () -> config.quickNav.button3.render, + newValue -> config.quickNav.button3.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button3.item.itemName, + () -> config.quickNav.button3.item.itemName, + newValue -> config.quickNav.button3.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button3.item.nbt, + () -> config.quickNav.button3.item.nbt, + newValue -> config.quickNav.button3.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button3.uiTitle, + () -> config.quickNav.button3.uiTitle, + newValue -> config.quickNav.button3.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button4")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button4.render, + () -> config.quickNav.button4.render, + newValue -> config.quickNav.button4.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button4.item.itemName, + () -> config.quickNav.button4.item.itemName, + newValue -> config.quickNav.button4.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button4.item.nbt, + () -> config.quickNav.button4.item.nbt, + newValue -> config.quickNav.button4.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button4.uiTitle, + () -> config.quickNav.button4.uiTitle, + newValue -> config.quickNav.button4.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button5")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button5.render, + () -> config.quickNav.button5.render, + newValue -> config.quickNav.button5.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button5.item.itemName, + () -> config.quickNav.button5.item.itemName, + newValue -> config.quickNav.button5.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button5.item.nbt, + () -> config.quickNav.button5.item.nbt, + newValue -> config.quickNav.button5.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button5.uiTitle, + () -> config.quickNav.button5.uiTitle, + newValue -> config.quickNav.button5.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button6")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button6.render, + () -> config.quickNav.button6.render, + newValue -> config.quickNav.button6.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button6.item.itemName, + () -> config.quickNav.button6.item.itemName, + newValue -> config.quickNav.button6.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button6.item.nbt, + () -> config.quickNav.button6.item.nbt, + newValue -> config.quickNav.button6.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button6.uiTitle, + () -> config.quickNav.button6.uiTitle, + newValue -> config.quickNav.button6.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button6.clickEvent, + () -> config.quickNav.button6.clickEvent, + newValue -> config.quickNav.button6.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Button 7 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button7")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button7.render, + () -> config.quickNav.button7.render, + newValue -> config.quickNav.button7.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button7.item.itemName, + () -> config.quickNav.button7.item.itemName, + newValue -> config.quickNav.button7.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button7.item.nbt, + () -> config.quickNav.button7.item.nbt, + newValue -> config.quickNav.button7.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button7.uiTitle, + () -> config.quickNav.button7.uiTitle, + newValue -> config.quickNav.button7.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button7.clickEvent, + () -> config.quickNav.button7.clickEvent, + newValue -> config.quickNav.button7.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Button 8 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button8")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button8.render, + () -> config.quickNav.button8.render, + newValue -> config.quickNav.button8.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button8.item.itemName, + () -> config.quickNav.button8.item.itemName, + newValue -> config.quickNav.button8.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button8.item.nbt, + () -> config.quickNav.button8.item.nbt, + newValue -> config.quickNav.button8.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button8.uiTitle, + () -> config.quickNav.button8.uiTitle, + newValue -> config.quickNav.button8.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button8.clickEvent, + () -> config.quickNav.button8.clickEvent, + newValue -> config.quickNav.button8.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Button 9 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button9")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button9.render, + () -> config.quickNav.button9.render, + newValue -> config.quickNav.button9.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button9.item.itemName, + () -> config.quickNav.button9.item.itemName, + newValue -> config.quickNav.button9.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button9.item.nbt, + () -> config.quickNav.button9.item.nbt, + newValue -> config.quickNav.button9.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button9.uiTitle, + () -> config.quickNav.button9.uiTitle, + newValue -> config.quickNav.button9.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button9.clickEvent, + () -> config.quickNav.button9.clickEvent, + newValue -> config.quickNav.button9.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Button 10 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button10")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button10.render, + () -> config.quickNav.button10.render, + newValue -> config.quickNav.button10.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button10.item.itemName, + () -> config.quickNav.button10.item.itemName, + newValue -> config.quickNav.button10.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button10.item.nbt, + () -> config.quickNav.button10.item.nbt, + newValue -> config.quickNav.button10.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button10.uiTitle, + () -> config.quickNav.button10.uiTitle, + newValue -> config.quickNav.button10.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button10.clickEvent, + () -> config.quickNav.button10.clickEvent, + newValue -> config.quickNav.button10.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Button 11 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button11")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button11.render, + () -> config.quickNav.button11.render, + newValue -> config.quickNav.button11.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button11.item.itemName, + () -> config.quickNav.button11.item.itemName, + newValue -> config.quickNav.button11.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button11.item.nbt, + () -> config.quickNav.button11.item.nbt, + newValue -> config.quickNav.button11.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button11.uiTitle, + () -> config.quickNav.button11.uiTitle, + newValue -> config.quickNav.button11.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button11.clickEvent, + () -> config.quickNav.button11.clickEvent, + newValue -> config.quickNav.button11.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + //Button 12 + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button12")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.render")) + .binding(defaults.quickNav.button12.render, + () -> config.quickNav.button12.render, + newValue -> config.quickNav.button12.render = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) + .binding(defaults.quickNav.button12.item.itemName, + () -> config.quickNav.button12.item.itemName, + newValue -> config.quickNav.button12.item.itemName = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) + .binding(defaults.quickNav.button12.item.nbt, + () -> config.quickNav.button12.item.nbt, + newValue -> config.quickNav.button12.item.nbt = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.uiTitle")) + .binding(defaults.quickNav.button12.uiTitle, + () -> config.quickNav.button12.uiTitle, + newValue -> config.quickNav.button12.uiTitle = newValue) + .controller(StringControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) + .binding(defaults.quickNav.button12.clickEvent, + () -> config.quickNav.button12.clickEvent, + newValue -> config.quickNav.button12.clickEvent = newValue) + .controller(StringControllerBuilder::create) + .build()) + .build()) + + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java new file mode 100644 index 00000000..2d8b1332 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/categories/SlayersCategory.java @@ -0,0 +1,116 @@ +package de.hysky.skyblocker.config.categories; + +import de.hysky.skyblocker.config.ConfigUtils; +import de.hysky.skyblocker.config.SkyblockerConfig; +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.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; +import net.minecraft.text.Text; + +public class SlayersCategory { + + public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { + return ConfigCategory.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.category.slayer")) + + //Vampire Slayer + .group(OptionGroup.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableEffigyWaypoints")) + .binding(defaults.slayer.vampireSlayer.enableEffigyWaypoints, + () -> config.slayer.vampireSlayer.enableEffigyWaypoints, + newValue -> config.slayer.vampireSlayer.enableEffigyWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.compactEffigyWaypoints")) + .binding(defaults.slayer.vampireSlayer.compactEffigyWaypoints, + () -> config.slayer.vampireSlayer.compactEffigyWaypoints, + newValue -> config.slayer.vampireSlayer.compactEffigyWaypoints = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency.@Tooltip"))) + .binding(defaults.slayer.vampireSlayer.effigyUpdateFrequency, + () -> config.slayer.vampireSlayer.effigyUpdateFrequency, + newValue -> config.slayer.vampireSlayer.effigyUpdateFrequency = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableHolyIceIndicator")) + .binding(defaults.slayer.vampireSlayer.enableHolyIceIndicator, + () -> config.slayer.vampireSlayer.enableHolyIceIndicator, + newValue -> config.slayer.vampireSlayer.enableHolyIceIndicator = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceIndicatorTickDelay")) + .binding(defaults.slayer.vampireSlayer.holyIceIndicatorTickDelay, + () -> config.slayer.vampireSlayer.holyIceIndicatorTickDelay, + newValue -> config.slayer.vampireSlayer.holyIceIndicatorTickDelay = newValue) + .controller(IntegerFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency.@Tooltip"))) + .binding(defaults.slayer.vampireSlayer.holyIceUpdateFrequency, + () -> config.slayer.vampireSlayer.holyIceUpdateFrequency, + newValue -> config.slayer.vampireSlayer.holyIceUpdateFrequency = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableHealingMelonIndicator")) + .binding(defaults.slayer.vampireSlayer.enableHealingMelonIndicator, + () -> config.slayer.vampireSlayer.enableHealingMelonIndicator, + newValue -> config.slayer.vampireSlayer.enableHealingMelonIndicator = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.healingMelonHealthThreshold")) + .binding(defaults.slayer.vampireSlayer.healingMelonHealthThreshold, + () -> config.slayer.vampireSlayer.healingMelonHealthThreshold, + newValue -> config.slayer.vampireSlayer.healingMelonHealthThreshold = newValue) + .controller(FloatFieldControllerBuilder::create) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableSteakStakeIndicator")) + .binding(defaults.slayer.vampireSlayer.enableSteakStakeIndicator, + () -> config.slayer.vampireSlayer.enableSteakStakeIndicator, + newValue -> config.slayer.vampireSlayer.enableSteakStakeIndicator = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency.@Tooltip"))) + .binding(defaults.slayer.vampireSlayer.steakStakeUpdateFrequency, + () -> config.slayer.vampireSlayer.steakStakeUpdateFrequency, + newValue -> config.slayer.vampireSlayer.steakStakeUpdateFrequency = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableManiaIndicator")) + .binding(defaults.slayer.vampireSlayer.enableManiaIndicator, + () -> config.slayer.vampireSlayer.enableManiaIndicator, + newValue -> config.slayer.vampireSlayer.enableManiaIndicator = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency")) + .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency.@Tooltip"))) + .binding(defaults.slayer.vampireSlayer.maniaUpdateFrequency, + () -> config.slayer.vampireSlayer.maniaUpdateFrequency, + newValue -> config.slayer.vampireSlayer.maniaUpdateFrequency = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) + .build()) + .build()) + + .build(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownController.java b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownController.java new file mode 100644 index 00000000..0b9a809d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownController.java @@ -0,0 +1,93 @@ +package de.hysky.skyblocker.config.controllers; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.AbstractWidget; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.dropdown.AbstractDropdownController; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.function.Function; +import java.util.stream.Stream; + +public class EnumDropdownController> extends AbstractDropdownController { + /** + * The function used to convert enum constants to strings used for display, suggestion, and validation. Defaults to {@link Enum#toString}. + */ + protected final Function toString; + + protected EnumDropdownController(Option option, Function toString) { + super(option); + this.toString = toString; + } + + @Override + public String getString() { + return toString.apply(option().pendingValue()); + } + + @Override + public void setFromString(String value) { + option().requestSet(getEnumFromString(value)); + } + + /** + * Searches through enum constants for one whose {@link #toString} result equals {@code value} + * + * @return The enum constant associated with the {@code value} or the pending value if none are found + * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy + */ + private E getEnumFromString(String value) { + value = value.toLowerCase(); + for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { + if (toString.apply(constant).toLowerCase().equals(value)) return constant; + } + + return option().pendingValue(); + } + + @Override + public boolean isValueValid(String value) { + value = value.toLowerCase(); + for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { + if (toString.apply(constant).equals(value)) return true; + } + + return false; + } + + @Override + protected String getValidValue(String value, int offset) { + return getValidEnumConstants(value) + .skip(offset) + .findFirst() + .orElseGet(this::getString); + } + + /** + * Filters and sorts through enum constants for those whose {@link #toString} result equals {@code value} + * + * @return a sorted stream containing enum constants associated with the {@code value} + * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy + */ + @NotNull + protected Stream getValidEnumConstants(String value) { + String valueLowerCase = value.toLowerCase(); + return Arrays.stream(option().pendingValue().getDeclaringClass().getEnumConstants()) + .map(this.toString) + .filter(constant -> constant.toLowerCase().contains(valueLowerCase)) + .sorted((s1, s2) -> { + String s1LowerCase = s1.toLowerCase(); + String s2LowerCase = s2.toLowerCase(); + if (s1LowerCase.startsWith(valueLowerCase) && !s2LowerCase.startsWith(valueLowerCase)) return -1; + if (!s1LowerCase.startsWith(valueLowerCase) && s2LowerCase.startsWith(valueLowerCase)) return 1; + return s1.compareTo(s2); + }); + } + + @Override + public AbstractWidget provideWidget(YACLScreen screen, Dimension widgetDimension) { + return new EnumDropdownControllerElement<>(this, screen, widgetDimension); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilder.java b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilder.java new file mode 100644 index 00000000..d451a88c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilder.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.config.controllers; + +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.api.controller.ControllerBuilder; + +import java.util.function.Function; + +public interface EnumDropdownControllerBuilder> extends ControllerBuilder { + EnumDropdownControllerBuilder toString(Function toString); + + static > EnumDropdownControllerBuilder create(Option option) { + return new EnumDropdownControllerBuilderImpl<>(option); + } + + /** + * Creates a factory for {@link EnumDropdownControllerBuilder}s with the given function for converting enum constants to strings. + * Use this if a custom toString function for an enum is needed. + * Use it like this: + *
{@code Option.createBuilder().controller(createEnumDropdownControllerBuilder.getFactory(MY_CUSTOM_ENUM_TO_STRING_FUNCTION))}
+ * @param toString The function used to convert enum constants to strings used for display, suggestion, and validation + * @return a factory for {@link EnumDropdownControllerBuilder}s + * @param the enum type + */ + static > Function, ControllerBuilder> getFactory(Function toString) { + return opt -> EnumDropdownControllerBuilder.create(opt).toString(toString); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java new file mode 100644 index 00000000..8f6dbb2a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.config.controllers; + +import dev.isxander.yacl3.api.Controller; +import dev.isxander.yacl3.api.Option; +import dev.isxander.yacl3.impl.controller.AbstractControllerBuilderImpl; + +import java.util.function.Function; + +public class EnumDropdownControllerBuilderImpl> extends AbstractControllerBuilderImpl implements EnumDropdownControllerBuilder { + private Function toString = Enum::toString; + + public EnumDropdownControllerBuilderImpl(Option option) { + super(option); + } + + @Override + public EnumDropdownControllerBuilder toString(Function toString) { + this.toString = toString; + return this; + } + + @SuppressWarnings("UnstableApiUsage") + @Override + public Controller build() { + return new EnumDropdownController<>(option, toString); + } +} diff --git a/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerElement.java b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerElement.java new file mode 100644 index 00000000..2a8de609 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/config/controllers/EnumDropdownControllerElement.java @@ -0,0 +1,26 @@ +package de.hysky.skyblocker.config.controllers; + +import dev.isxander.yacl3.api.utils.Dimension; +import dev.isxander.yacl3.gui.YACLScreen; +import dev.isxander.yacl3.gui.controllers.dropdown.AbstractDropdownControllerElement; + +import java.util.List; + +public class EnumDropdownControllerElement> extends AbstractDropdownControllerElement { + private final EnumDropdownController controller; + + public EnumDropdownControllerElement(EnumDropdownController control, YACLScreen screen, Dimension dim) { + super(control, screen, dim); + this.controller = control; + } + + @Override + public List computeMatchingValues() { + return controller.getValidEnumConstants(inputField).toList(); + } + + @Override + public String getString(String object) { + return object; + } +} diff --git a/src/main/java/de/hysky/skyblocker/events/ClientPlayerBlockBreakEvent.java b/src/main/java/de/hysky/skyblocker/events/ClientPlayerBlockBreakEvent.java new file mode 100644 index 00000000..83ac716f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/events/ClientPlayerBlockBreakEvent.java @@ -0,0 +1,23 @@ +package de.hysky.skyblocker.events; + +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +// Fabric API currently doesn't have an event for this +public class ClientPlayerBlockBreakEvent { + public static final Event AFTER = EventFactory.createArrayBacked(AfterBlockBreak.class, + (listeners) -> (world, player, pos, state) -> { + for (AfterBlockBreak listener : listeners) { + listener.afterBlockBreak(world, player, pos, state); + } + }); + + @FunctionalInterface + public interface AfterBlockBreak { + void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state); + } +} diff --git a/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java b/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java new file mode 100644 index 00000000..303e454f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/events/SkyblockEvents.java @@ -0,0 +1,33 @@ +package de.hysky.skyblocker.events; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; + +@Environment(EnvType.CLIENT) +public final class SkyblockEvents { + public static final Event JOIN = EventFactory.createArrayBacked(SkyblockEvents.SkyblockJoin.class, callbacks -> () -> { + for (SkyblockEvents.SkyblockJoin callback : callbacks) { + callback.onSkyblockJoin(); + } + }); + + public static final Event LEAVE = EventFactory.createArrayBacked(SkyblockEvents.SkyblockLeave.class, callbacks -> () -> { + for (SkyblockEvents.SkyblockLeave callback : callbacks) { + callback.onSkyblockLeave(); + } + }); + + @Environment(EnvType.CLIENT) + @FunctionalInterface + public interface SkyblockJoin { + void onSkyblockJoin(); + } + + @Environment(EnvType.CLIENT) + @FunctionalInterface + public interface SkyblockLeave { + void onSkyblockLeave(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/AbstractInventoryScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixin/AbstractInventoryScreenMixin.java new file mode 100644 index 00000000..d0d4b9f7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/AbstractInventoryScreenMixin.java @@ -0,0 +1,19 @@ +package de.hysky.skyblocker.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen; + +@Mixin(AbstractInventoryScreen.class) +public class AbstractInventoryScreenMixin { + + @Inject(method = "drawStatusEffects", at = @At("HEAD"), cancellable = true) + private void skyblocker$dontDrawStatusEffects(CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hideStatusEffectOverlay) ci.cancel(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java new file mode 100644 index 00000000..02d75409 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ArmorTrimMixin.java @@ -0,0 +1,37 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.CustomArmorTrims; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.item.ItemStack; +import net.minecraft.item.trim.ArmorTrim; +import net.minecraft.nbt.NbtCompound; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.Optional; + +@Mixin(ArmorTrim.class) +public class ArmorTrimMixin { + + @ModifyReturnValue(method = "getTrim", at = @At("RETURN")) + private static Optional skyblocker$customArmorTrims(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional original, @Local ItemStack stack) { + NbtCompound nbt = stack.getNbt(); + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + Object2ObjectOpenHashMap customTrims = SkyblockerConfigManager.get().general.customArmorTrims; + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + if (customTrims.containsKey(itemUuid)) { + CustomArmorTrims.ArmorTrimId trimKey = customTrims.get(itemUuid); + return CustomArmorTrims.TRIMS_CACHE.getOrDefault(trimKey, original); + } + } + + return original; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java new file mode 100644 index 00000000..dc2fa673 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.passive.BatEntity; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(BatEntity.class) +public abstract class BatEntityMixin extends AmbientEntity { + protected BatEntityMixin(EntityType entityType, World world) { + super(entityType, world); + } + + @Override + public void onRemoved() { + super.onRemoved(); + DungeonSecrets.onBatRemoved(this); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java new file mode 100644 index 00000000..fff534b2 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java @@ -0,0 +1,48 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.WrapWithCondition; +import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; +import dev.cbyrne.betterinject.annotations.Inject; +import de.hysky.skyblocker.skyblock.FishingHelper; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(ClientPlayNetworkHandler.class) +public abstract class ClientPlayNetworkHandlerMixin { + + @Inject(method = "onPlaySound", at = @At("RETURN")) + private void skyblocker$onPlaySound(PlaySoundS2CPacket packet) { + FishingHelper.onSound(packet); + } + + @SuppressWarnings("resource") + @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "STORE", ordinal = 0)) + private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity, @Local LivingEntity collector) { + DungeonSecrets.onItemPickup(itemEntity, collector, collector == MinecraftClient.getInstance().player); + return itemEntity; + } + + @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) + private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) { + return !Utils.isOnHypixel(); + } + + @WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelPlayerListWarning(Logger instance, String format, Object arg) { + return !Utils.isOnHypixel(); + } + + @WrapWithCondition(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { + return !Utils.isOnHypixel(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java new file mode 100644 index 00000000..37ae92e8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.mixin; + +import com.mojang.authlib.GameProfile; + +import dev.cbyrne.betterinject.annotations.Inject; +import de.hysky.skyblocker.skyblock.HotbarSlotLock; +import de.hysky.skyblocker.skyblock.item.ItemProtection; +import de.hysky.skyblocker.skyblock.rift.HealingMelonIndicator; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ClientPlayerEntity.class) +public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity { + public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) { + super(world, profile); + } + + @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) + public void skyblocker$dropSelectedItem(CallbackInfoReturnable cir) { + if (Utils.isOnSkyblock()) { + if (ItemProtection.isItemProtected(this.getInventory().getMainHandStack())) cir.setReturnValue(false); + HotbarSlotLock.handleDropSelectedItem(this.getInventory().selectedSlot, cir); + } + } + + @Inject(method = "updateHealth", at = @At("RETURN")) + public void skyblocker$updateHealth() { + HealingMelonIndicator.updateHealth(); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java new file mode 100644 index 00000000..fab9a1ea --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.events.ClientPlayerBlockBreakEvent; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerInteractionManager; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(ClientPlayerInteractionManager.class) +public class ClientPlayerInteractionManagerMixin { + @Shadow + @Final + private MinecraftClient client; + + @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V"), locals = LocalCapture.CAPTURE_FAILHARD) + private void skyblocker$onBlockBroken(BlockPos pos, CallbackInfoReturnable cir, World world, BlockState blockState) { + ClientPlayerBlockBreakEvent.AFTER.invoker().afterBlockBreak(world, this.client.player, pos, blockState); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/DrawContextMixin.java b/src/main/java/de/hysky/skyblocker/mixin/DrawContextMixin.java new file mode 100644 index 00000000..41b8e985 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/DrawContextMixin.java @@ -0,0 +1,72 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import dev.cbyrne.betterinject.annotations.Arg; +import dev.cbyrne.betterinject.annotations.Inject; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.AttributeShards; +import de.hysky.skyblocker.skyblock.item.ItemCooldowns; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; +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; + +@Mixin(DrawContext.class) +public abstract class DrawContextMixin { + @Shadow + @Final + private MatrixStack matrices; + + @Shadow + public abstract int drawText(TextRenderer textRenderer, @Nullable String text, int x, int y, int color, boolean shadow); + + @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD")) + private void skyblocker$renderAttributeShardDisplay(@Arg TextRenderer textRenderer, @Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y, @Local(argsOnly = true) LocalRef countOverride) { + if (!SkyblockerConfigManager.get().general.itemInfoDisplay.attributeShardInfo) return; + + NbtCompound nbt = stack.getNbt(); + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + + if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) { + NbtCompound attributesTag = extraAttributes.getCompound("attributes"); + String[] attributes = attributesTag.getKeys().toArray(String[]::new); + + if (attributes.length != 0) { + String attributeId = attributes[0]; + int attributeLevel = attributesTag.getInt(attributeId); + + //Set item count + countOverride.set(Integer.toString(attributeLevel)); + + //Draw the attribute name + this.matrices.push(); + this.matrices.translate(0f, 0f, 200f); + + String attributeInitials = AttributeShards.getShortName(attributeId); + + this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true); + + this.matrices.pop(); + } + } + } + } + + @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) + private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local ItemStack stack) { + return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/DyeableItemMixin.java b/src/main/java/de/hysky/skyblocker/mixin/DyeableItemMixin.java new file mode 100644 index 00000000..51ab3852 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/DyeableItemMixin.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.item.DyeableItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(DyeableItem.class) +public interface DyeableItemMixin { + @ModifyReturnValue(method = "getColor", at = @At("RETURN")) + private int skyblocker$customDyeColor(int originalColor, ItemStack stack) { + NbtCompound nbt = stack.getNbt(); + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + return SkyblockerConfigManager.get().general.customDyeColors.getOrDefault(itemUuid, originalColor); + } + + return originalColor; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/FarmlandBlockMixin.java b/src/main/java/de/hysky/skyblocker/mixin/FarmlandBlockMixin.java new file mode 100644 index 00000000..dfa886c4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/FarmlandBlockMixin.java @@ -0,0 +1,38 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.FarmlandBlock; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.shape.VoxelShape; +import net.minecraft.util.shape.VoxelShapes; +import net.minecraft.world.BlockView; +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; + +@Mixin(FarmlandBlock.class) +public abstract class FarmlandBlockMixin extends Block { + @Shadow + @Final + protected static VoxelShape SHAPE; + + protected FarmlandBlockMixin(Settings settings) { + super(settings); + } + + @ModifyReturnValue(method = "getOutlineShape", at = @At("RETURN")) + private VoxelShape skyblocker$replaceOutlineShape(VoxelShape original) { + return Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hitbox.oldFarmlandHitbox ? VoxelShapes.fullCube() : original; + } + + @SuppressWarnings("deprecation") + @Override + public VoxelShape getCullingShape(BlockState state, BlockView world, BlockPos pos) { + return SHAPE; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/GenericContainerScreenHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/GenericContainerScreenHandlerMixin.java new file mode 100644 index 00000000..9929c5d4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/GenericContainerScreenHandlerMixin.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.SkyblockerMod; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; + +import java.util.List; + +@Mixin(GenericContainerScreenHandler.class) +public abstract class GenericContainerScreenHandlerMixin extends ScreenHandler { + protected GenericContainerScreenHandlerMixin(@Nullable ScreenHandlerType type, int syncId) { + super(type, syncId); + } + + @Override + public void setStackInSlot(int slot, int revision, ItemStack stack) { + SkyblockerMod.getInstance().containerSolverManager.markDirty(); + super.setStackInSlot(slot, revision, stack); + } + + @Override + public void updateSlotStacks(int revision, List stacks, ItemStack cursorStack) { + SkyblockerMod.getInstance().containerSolverManager.markDirty(); + super.updateSlotStacks(revision, stacks, cursorStack); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java new file mode 100644 index 00000000..689974c8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/HandledScreenMixin.java @@ -0,0 +1,193 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; +import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; +import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; +import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver; +import de.hysky.skyblocker.skyblock.item.BackpackPreview; +import de.hysky.skyblocker.skyblock.item.CompactorDeletorPreview; +import de.hysky.skyblocker.skyblock.item.ItemProtection; +import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds; +import de.hysky.skyblocker.skyblock.item.WikiLookup; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; +import net.minecraft.screen.slot.SlotActionType; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.ModifyVariable; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.Map; +import java.util.regex.Matcher; + +@Mixin(HandledScreen.class) +public abstract class HandledScreenMixin extends Screen { + /** + * This is the slot id returned for when a click is outside of the screen's bounds + */ + @Unique + private static final int OUT_OF_BOUNDS_SLOT = -999; + + @Shadow + @Nullable + protected Slot focusedSlot; + + @Shadow + @Final + protected T handler; + + protected HandledScreenMixin(Text title) { + super(title); + } + + @Inject(at = @At("HEAD"), method = "keyPressed") + public void skyblocker$keyPressed(int keyCode, int scanCode, int modifiers, CallbackInfoReturnable cir) { + if (this.client != null && this.focusedSlot != null && keyCode != 256 && !this.client.options.inventoryKey.matchesKey(keyCode, scanCode) && WikiLookup.wikiLookup.matchesKey(keyCode, scanCode)) { + WikiLookup.openWiki(this.focusedSlot); + } + } + + @SuppressWarnings("DataFlowIssue") + // makes intellij be quiet about this.focusedSlot maybe being null. It's already null checked in mixined method. + @Inject(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V"), cancellable = true) + public void skyblocker$drawMouseOverTooltip(DrawContext context, int x, int y, CallbackInfo ci) { + if (!Utils.isOnSkyblock()) return; + + // Hide Empty Tooltips + if (SkyblockerConfigManager.get().general.hideEmptyTooltips && focusedSlot.getStack().getName().getString().equals(" ")) { + ci.cancel(); + } + + // Backpack Preview + boolean shiftDown = SkyblockerConfigManager.get().general.backpackPreviewWithoutShift ^ Screen.hasShiftDown(); + if (shiftDown && getTitle().getString().equals("Storage") && focusedSlot.inventory != client.player.getInventory() && BackpackPreview.renderPreview(context, focusedSlot.getIndex(), x, y)) { + ci.cancel(); + } + + // Compactor Preview + if (SkyblockerConfigManager.get().general.compactorDeletorPreview) { + ItemStack stack = focusedSlot.getStack(); + Matcher matcher = CompactorDeletorPreview.NAME.matcher(ItemRegistry.getInternalName(stack)); + if (matcher.matches() && CompactorDeletorPreview.drawPreview(context, stack, matcher.group("type"), matcher.group("size"), x, y)) { + ci.cancel(); + } + } + } + + @Redirect(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/slot/Slot;getStack()Lnet/minecraft/item/ItemStack;", ordinal = 0)) + private ItemStack skyblocker$experimentSolvers$replaceTooltipDisplayStack(Slot slot) { + return skyblocker$experimentSolvers$getStack(slot, null); + } + + @ModifyVariable(method = "drawSlot", at = @At(value = "LOAD", ordinal = 4), ordinal = 0) + private ItemStack skyblocker$experimentSolvers$replaceDisplayStack(ItemStack stack, DrawContext context, Slot slot) { + return skyblocker$experimentSolvers$getStack(slot, stack); + } + + + @Unique + private ItemStack skyblocker$experimentSolvers$getStack(Slot slot, ItemStack stack) { + ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver(); + if ((currentSolver instanceof SuperpairsSolver || currentSolver instanceof UltrasequencerSolver) && ((ExperimentSolver) currentSolver).getState() == ExperimentSolver.State.SHOW && slot.inventory instanceof SimpleInventory) { + ItemStack itemStack = ((ExperimentSolver) currentSolver).getSlots().get(slot.getIndex()); + return itemStack == null ? slot.getStack() : itemStack; + } + return (stack != null) ? stack : slot.getStack(); + } + + @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;clickSlot(IIILnet/minecraft/screen/slot/SlotActionType;Lnet/minecraft/entity/player/PlayerEntity;)V")) + private void skyblocker$experimentSolvers$onSlotClick(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { + if (slot != null) { + ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver(); + if (currentSolver instanceof ExperimentSolver experimentSolver && experimentSolver.getState() == ExperimentSolver.State.SHOW && slot.inventory instanceof SimpleInventory) { + if (experimentSolver instanceof ChronomatronSolver chronomatronSolver) { + Item item = chronomatronSolver.getChronomatronSlots().get(chronomatronSolver.getChronomatronCurrentOrdinal()); + if ((slot.getStack().isOf(item) || ChronomatronSolver.TERRACOTTA_TO_GLASS.get(slot.getStack().getItem()) == item) && chronomatronSolver.incrementChronomatronCurrentOrdinal() >= chronomatronSolver.getChronomatronSlots().size()) { + chronomatronSolver.setState(ExperimentSolver.State.END); + } + } else if (experimentSolver instanceof SuperpairsSolver superpairsSolver) { + superpairsSolver.setSuperpairsPrevClickedSlot(slot.getIndex()); + superpairsSolver.setSuperpairsCurrentSlot(ItemStack.EMPTY); + } else if (experimentSolver instanceof UltrasequencerSolver ultrasequencerSolver && slot.getIndex() == ultrasequencerSolver.getUltrasequencerNextSlot()) { + int count = ultrasequencerSolver.getSlots().get(ultrasequencerSolver.getUltrasequencerNextSlot()).getCount() + 1; + ultrasequencerSolver.getSlots().entrySet().stream().filter(entry -> entry.getValue().getCount() == count).findAny().map(Map.Entry::getKey).ifPresentOrElse(ultrasequencerSolver::setUltrasequencerNextSlot, () -> ultrasequencerSolver.setState(ExperimentSolver.State.END)); + } + } + } + } + + /** + * 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 + */ + @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) + private void skyblocker$onSlotInteract(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { + if (Utils.isOnSkyblock()) { + // When you try and drop the item by picking it up then clicking outside of the screen + if (slotId == OUT_OF_BOUNDS_SLOT) { + ItemStack cursorStack = this.handler.getCursorStack(); + + if (ItemProtection.isItemProtected(cursorStack)) ci.cancel(); + } + + if (slot != null) { + // When you click your drop key while hovering over an item + if (actionType == SlotActionType.THROW) { + ItemStack stack = slot.getStack(); + + if (ItemProtection.isItemProtected(stack)) ci.cancel(); + } + + //Prevent salvaging + if (this.getTitle().getString().equals("Salvage Items")) { + ItemStack stack = slot.getStack(); + + if (ItemProtection.isItemProtected(stack)) ci.cancel(); + } + + //Prevent selling to NPC shops + if (this.client != null && this.handler instanceof GenericContainerScreenHandler genericContainerScreenHandler && genericContainerScreenHandler.getRows() == 6) { + ItemStack sellItem = this.handler.slots.get(49).getStack(); + + if (sellItem.getName().getString().equals("Sell Item") || skyblocker$doesLoreContain(sellItem, this.client, "buyback")) { + ItemStack stack = slot.getStack(); + + if (ItemProtection.isItemProtected(stack)) ci.cancel(); + } + } + } + } + } + + //TODO make this a util method somewhere else, eventually + private static boolean skyblocker$doesLoreContain(ItemStack stack, MinecraftClient client, String searchString) { + return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).anyMatch(line -> line.contains(searchString)); + } + + @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) + private void skyblocker$drawItemRarityBackground(DrawContext context, Slot slot, CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(slot.getStack(), context, slot.x, slot.y); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java new file mode 100644 index 00000000..1b6d62d4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/InGameHudMixin.java @@ -0,0 +1,93 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.FancyStatusBars; +import de.hysky.skyblocker.skyblock.HotbarSlotLock; +import de.hysky.skyblocker.skyblock.item.ItemCooldowns; +import de.hysky.skyblocker.skyblock.dungeon.DungeonMap; +import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Environment(EnvType.CLIENT) +@Mixin(InGameHud.class) +public abstract class InGameHudMixin { + @Unique + private static final Identifier SLOT_LOCK = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/slot_lock.png"); + @Unique + private final FancyStatusBars statusBars = new FancyStatusBars(); + + @Shadow + private int scaledHeight; + @Shadow + private int scaledWidth; + + @Shadow + @Final + private MinecraftClient client; + + @Inject(method = "renderHotbar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHotbarItem(Lnet/minecraft/client/gui/DrawContext;IIFLnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;I)V", ordinal = 0)) + public void skyblocker$renderHotbarItemLockOrRarityBg(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y, @Local PlayerEntity player) { + if (Utils.isOnSkyblock()) { + if (SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(player.getInventory().main.get(index), context, x, y); + if (HotbarSlotLock.isLocked(index)) context.drawTexture(SLOT_LOCK, x, y, 0, 0, 16, 16); + } + } + + @Inject(method = "renderExperienceBar", at = @At("HEAD"), cancellable = true) + private void skyblocker$renderExperienceBar(CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.bars.enableBars && !Utils.isInTheRift()) + ci.cancel(); + } + + @Inject(method = "renderStatusBars", at = @At("HEAD"), cancellable = true) + private void skyblocker$renderStatusBars(DrawContext context, CallbackInfo ci) { + if (!Utils.isOnSkyblock()) + return; + if (statusBars.render(context, scaledWidth, scaledHeight)) + ci.cancel(); + + if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.enableMap) + DungeonMap.render(context.getMatrices()); + } + + @Inject(method = "renderMountHealth", at = @At("HEAD"), cancellable = true) + private void skyblocker$renderMountHealth(CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.bars.enableBars && !Utils.isInTheRift()) + ci.cancel(); + } + + @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"), cancellable = true) + private void skyblocker$dontRenderStatusEffects(CallbackInfo ci) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hideStatusEffectOverlay) ci.cancel(); + } + + @ModifyExpressionValue(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getAttackCooldownProgress(F)F")) + private float skyblocker$modifyAttackIndicatorCooldown(float cooldownProgress) { + if (Utils.isOnSkyblock() && client.player != null) { + ItemStack stack = client.player.getMainHandStack(); + if (ItemCooldowns.isOnCooldown(stack)) { + return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); + } + } + + return cooldownProgress; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/InventoryScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixin/InventoryScreenMixin.java new file mode 100644 index 00000000..8e6b9230 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/InventoryScreenMixin.java @@ -0,0 +1,18 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.itemlist.ItemListWidget; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.gui.screen.ingame.InventoryScreen; +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(InventoryScreen.class) +public abstract class InventoryScreenMixin { + @ModifyExpressionValue(method = "", at = @At(value = "NEW", target = "net/minecraft/client/gui/screen/recipebook/RecipeBookWidget")) + private RecipeBookWidget skyblocker$replaceRecipeBook(RecipeBookWidget original) { + return SkyblockerConfigManager.get().general.itemList.enableItemList && Utils.isOnSkyblock() ? new ItemListWidget() : original; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ItemMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ItemMixin.java new file mode 100644 index 00000000..98bea52b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ItemMixin.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.mixin; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +@Mixin(Item.class) +public abstract class ItemMixin { + @WrapOperation( + method = {"getItemBarColor", "getItemBarStep"}, + at = @At(value = "FIELD", target = "Lnet/minecraft/item/Item;maxDamage:I", opcode = Opcodes.GETFIELD) + ) + private int skyblocker$handlePickoDrillBar(Item item, Operation original, ItemStack stack) { + return stack.getMaxDamage(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java new file mode 100644 index 00000000..c7f5fac9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java @@ -0,0 +1,61 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.utils.ItemUtils; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; + +@Mixin(ItemStack.class) +public abstract class ItemStackMixin { + @Shadow + @Nullable + private NbtCompound nbt; + + @ModifyReturnValue(method = "getName", at = @At("RETURN")) + private Text skyblocker$customItemNames(Text original) { + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + return SkyblockerConfigManager.get().general.customItemNames.getOrDefault(itemUuid, original); + } + + return original; + } + + @ModifyReturnValue(method = "getDamage", at = @At("RETURN")) + private int skyblocker$handleDamage(int original) { + ItemUtils.Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + if (dur != null) { + return dur.max() - dur.current(); + } + return original; + } + + @ModifyReturnValue(method = "getMaxDamage", at = @At("RETURN")) + private int skyblocker$handleMaxDamage(int original) { + ItemUtils.Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + if (dur != null) { + return dur.max(); + } + return original; + } + + @ModifyReturnValue(method = "isDamageable", at = @At("RETURN")) + private boolean skyblocker$handleDamageable(boolean original) { + ItemUtils.Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + if (dur != null) { + return true; + } + return original; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/LeverBlockMixin.java b/src/main/java/de/hysky/skyblocker/mixin/LeverBlockMixin.java new file mode 100644 index 00000000..97c0a7c0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/LeverBlockMixin.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.skyblock.dungeon.OldLever; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.block.BlockState; +import net.minecraft.block.LeverBlock; +import net.minecraft.block.WallMountedBlock; +import net.minecraft.util.shape.VoxelShape; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import dev.cbyrne.betterinject.annotations.Arg; +import dev.cbyrne.betterinject.annotations.Inject; + +@Mixin(LeverBlock.class) +public abstract class LeverBlockMixin extends WallMountedBlock { + protected LeverBlockMixin(Settings settings) { + super(settings); + } + + @Inject(method = "getOutlineShape", at = @At("HEAD"), cancellable = true) + public void skyblocker$onGetOutlineShape(@Arg BlockState state, CallbackInfoReturnable cir) { + if (Utils.isOnSkyblock()) { + VoxelShape shape = OldLever.getShape(state.get(FACE), state.get(FACING)); + if (shape != null) cir.setReturnValue(shape); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java b/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java new file mode 100644 index 00000000..066490d5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/MinecraftClientMixin.java @@ -0,0 +1,25 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.skyblock.HotbarSlotLock; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import dev.cbyrne.betterinject.annotations.Inject; + +@Mixin(MinecraftClient.class) +public abstract class MinecraftClientMixin { + @Shadow + @Nullable + public ClientPlayerEntity player; + + @Inject(method = "handleInputEvents", at = @At("HEAD")) + public void skyblocker$handleInputEvents() { + if (Utils.isOnSkyblock()) { + HotbarSlotLock.handleInputEvents(player); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/mixin/PlayerListHudMixin.java b/src/main/java/de/hysky/skyblocker/mixin/PlayerListHudMixin.java new file mode 100644 index 00000000..7330b1c1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/PlayerListHudMixin.java @@ -0,0 +1,57 @@ +package de.hysky.skyblocker.mixin; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.TabHud; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.hud.PlayerListHud; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.text.Text; +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.callback.CallbackInfo; + +import dev.cbyrne.betterinject.annotations.Arg; +import dev.cbyrne.betterinject.annotations.Inject; + +@Environment(EnvType.CLIENT) +@Mixin(PlayerListHud.class) +public class PlayerListHudMixin { + @Shadow + private Text footer; + + @Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/client/gui/DrawContext;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreboardObjective;)V", cancellable = true) + public void skyblocker$renderTabHud(@Arg DrawContext context, @Arg int w, CallbackInfo info) { + if (!Utils.isOnSkyblock() || !SkyblockerConfigManager.get().general.tabHud.tabHudEnabled || TabHud.defaultTgl.isPressed()) { + return; + } + + ClientPlayNetworkHandler nwH = MinecraftClient.getInstance().getNetworkHandler(); + if (nwH == null) { + return; + } + + int h = MinecraftClient.getInstance().getWindow().getScaledHeight(); + float scale = SkyblockerConfigManager.get().general.tabHud.tabHudScale / 100f; + w = (int) (w / scale); + h = (int) (h / scale); + + PlayerListMgr.updateFooter(footer); + + try { + ScreenMaster.render(context, w,h); + // Screen screen = Screen.getCorrect(w, h, footer); + // screen.render(context); + info.cancel(); + } catch (Exception e) { + TabHud.LOGGER.error("[Skyblocker] Encountered unknown exception while drawing default hud", e); + } + } + +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinProviderMixin.java b/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinProviderMixin.java new file mode 100644 index 00000000..978835d2 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/PlayerSkinProviderMixin.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.ModifyReturnValue; +import com.llamalad7.mixinextras.sugar.Local; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.minecraft.MinecraftSessionService; + +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.texture.PlayerSkinProvider.Textures; + +@Mixin(targets = "net.minecraft.client.texture.PlayerSkinProvider$1") +public class PlayerSkinProviderMixin { + + @ModifyReturnValue(method = "method_52867", at = @At("RETURN")) + private static Textures skyblocker$fixTexturesThatHadAnInvalidSignature(Textures texture, @Local MinecraftSessionService sessionService, @Local GameProfile profile) { + if (Utils.isOnHypixel() && texture == Textures.MISSING) { + try { + return Textures.fromMap(sessionService.getTextures(profile, false), false); + } catch (Throwable t) { + return Textures.MISSING; + } + } + + return texture; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/ScoreboardMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ScoreboardMixin.java new file mode 100644 index 00000000..2cfb658a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/ScoreboardMixin.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.WrapWithCondition; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.scoreboard.Scoreboard; +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +@Mixin(Scoreboard.class) +public abstract class ScoreboardMixin { + @WrapWithCondition(method = "addTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object arg) { + return !Utils.isOnHypixel(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java b/src/main/java/de/hysky/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java new file mode 100644 index 00000000..3a60bfbb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java @@ -0,0 +1,24 @@ +package de.hysky.skyblocker.mixin; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; + +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.gui.screen.multiplayer.SocialInteractionsPlayerListEntry; +import net.minecraft.client.gui.screen.multiplayer.SocialInteractionsPlayerListWidget; + +@Mixin(SocialInteractionsPlayerListWidget.class) +public class SocialInteractionsPlayerListWidgetMixin { + + @WrapOperation(method = "setPlayers", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", remap = false)) + private Object skyblocker$hideInvalidPlayers(Map map, Object uuid, Object entry, Operation operation) { + if (Utils.isOnSkyblock() && !((SocialInteractionsPlayerListEntry) entry).getName().matches("[A-Za-z0-9_]+")) return null; + + return operation.call(map, uuid, entry); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/WorldRendererMixin.java b/src/main/java/de/hysky/skyblocker/mixin/WorldRendererMixin.java new file mode 100644 index 00000000..e723c998 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/WorldRendererMixin.java @@ -0,0 +1,33 @@ +package de.hysky.skyblocker.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.dungeon.StarredMobGlow; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.entity.Entity; + +@Mixin(WorldRenderer.class) +public class WorldRendererMixin { + + @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;hasOutline(Lnet/minecraft/entity/Entity;)Z")) + private boolean skyblocker$shouldStarredMobGlow(boolean original, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) { + boolean isAStarredMobThatShouldGlow = SkyblockerConfigManager.get().locations.dungeons.starredMobGlow && StarredMobGlow.shouldMobGlow(entity); + + isGlowingStarredMob.set(isAStarredMobThatShouldGlow); + + return original || isAStarredMobThatShouldGlow; + } + + @ModifyVariable(method = "render", at = @At("STORE"), ordinal = 0) + private int skyblocker$modifyGlowColor(int color, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) { + return isGlowingStarredMob.get() ? StarredMobGlow.getGlowColor(entity) : color; + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java b/src/main/java/de/hysky/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java new file mode 100644 index 00000000..8da87be0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java @@ -0,0 +1,20 @@ +package de.hysky.skyblocker.mixin; + +import org.slf4j.Logger; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; + +import de.hysky.skyblocker.utils.Utils; + +@Mixin(value = YggdrasilMinecraftSessionService.class, remap = false) +public class YggdrasilMinecraftSessionServiceMixin { + + @WrapOperation(method = "getSecurePropertyValue", remap = false, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private void skyblocker$dontLogMissingSignaturesOrTamperedProperties(Logger logger, String message, Object property, Operation operation) { + if (!Utils.isOnHypixel()) operation.call(logger, message, property); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java b/src/main/java/de/hysky/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java new file mode 100644 index 00000000..d38e40cc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java @@ -0,0 +1,59 @@ +package de.hysky.skyblocker.mixin; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.mojang.authlib.yggdrasil.YggdrasilServicesKeyInfo; + +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; +import de.hysky.skyblocker.utils.Utils; +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.Unique; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.Base64; +import java.util.Map; + +@Mixin(value = YggdrasilServicesKeyInfo.class, remap = false) +public class YggdrasilServicesKeyInfoMixin { + @Shadow + @Final + private static Logger LOGGER; + @Unique + private static final Map REPLACEMENT_MAP = Map.of(); + @Unique + private static final IntList ERRONEUS_SIGNATURE_HASHES = new IntArrayList(); + + @WrapOperation(method = "validateProperty", at = @At(value = "INVOKE", target = "Ljava/util/Base64$Decoder;decode(Ljava/lang/String;)[B", remap = false), remap = false) + private byte[] skyblocker$replaceKnownWrongBase64(Base64.Decoder decoder, String signature, Operation decode) { + try { + return decode.call(decoder, signature); + } catch (IllegalArgumentException e) { + try { + return decode.call(decoder, signature.replaceAll("[^A-Za-z0-9+/=]", "")); + } catch (IllegalArgumentException e2) { + if (Utils.isOnSkyblock()) { + if (REPLACEMENT_MAP.containsKey(signature)) { + return decode.call(decoder, REPLACEMENT_MAP.get(signature)); + } + int signatureHashCode = signature.hashCode(); + if (!ERRONEUS_SIGNATURE_HASHES.contains(signatureHashCode)) { + ERRONEUS_SIGNATURE_HASHES.add(signatureHashCode); + LOGGER.warn("[Skyblocker Base64 Fixer] Failed to decode base64 string No.{}: {}", ERRONEUS_SIGNATURE_HASHES.size() - 1, signature); + } else { + LOGGER.warn("[Skyblocker Base64 Fixer] Failed to decode the base64 string No.{} again", ERRONEUS_SIGNATURE_HASHES.indexOf(signatureHashCode)); + } + } + } + throw e; + } + } + + @WrapOperation(method = "validateProperty", remap = false, at = @At(value = "INVOKE", target = "org/slf4j/Logger.error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) + private void skyblocker$dontLogFailedSignatureValidation(Logger logger, String message, Object property, Object exception, Operation operation) { + if (!Utils.isOnHypixel()) operation.call(logger, message, property, exception); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java new file mode 100644 index 00000000..0b607fce --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.mixin.accessor; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer; +import net.minecraft.client.util.math.MatrixStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(BeaconBlockEntityRenderer.class) +public interface BeaconBlockEntityRendererInvoker { + @SuppressWarnings("unused") + @Invoker("renderBeam") + static void renderBeam(MatrixStack matrices, VertexConsumerProvider vertexConsumers, float tickDelta, long worldTime, int yOffset, int maxY, float[] color) { + throw new IllegalStateException("Mixin invoker failed to apply."); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java new file mode 100644 index 00000000..8dcccf34 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/DrawContextInvoker.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.mixin.accessor; + +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.client.gui.tooltip.TooltipPositioner; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; + +@Mixin(DrawContext.class) +public interface DrawContextInvoker { + + @Invoker + void invokeDrawTooltip(TextRenderer textRenderer, List components, int x, int y, TooltipPositioner positioner); +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java new file mode 100644 index 00000000..3a9e688b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.mixin.accessor; + +import de.hysky.skyblocker.utils.render.FrustumUtils; +import net.minecraft.client.render.Frustum; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +/** + * Use {@link FrustumUtils#isVisible(double, double, double, double, double, double) FrustumUtils#isVisible} which is shorter. For the purpose of avoiding object allocations! + */ +@Mixin(Frustum.class) +public interface FrustumInvoker { + @Invoker + boolean invokeIsVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ); +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/HandledScreenAccessor.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/HandledScreenAccessor.java new file mode 100644 index 00000000..d82422cb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/HandledScreenAccessor.java @@ -0,0 +1,20 @@ +package de.hysky.skyblocker.mixin.accessor; + +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(HandledScreen.class) +public interface HandledScreenAccessor { + @Accessor("x") + int getX(); + + @Accessor("y") + int getY(); + + @Accessor + int getBackgroundWidth(); + + @Accessor + int getBackgroundHeight(); +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/PlayerListHudAccessor.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/PlayerListHudAccessor.java new file mode 100644 index 00000000..d82c568f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/PlayerListHudAccessor.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.mixin.accessor; + +import net.minecraft.client.gui.hud.PlayerListHud; +import net.minecraft.client.network.PlayerListEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Comparator; + +@Mixin(PlayerListHud.class) +public interface PlayerListHudAccessor { + + @Accessor("ENTRY_ORDERING") + static Comparator getOrdering() { + throw new AssertionError(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java new file mode 100644 index 00000000..aecdf9b7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.mixin.accessor; + +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(RecipeBookWidget.class) +public interface RecipeBookWidgetAccessor { + @Accessor + String getSearchText(); + @Accessor + TextFieldWidget getSearchField(); +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/ScreenAccessor.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/ScreenAccessor.java new file mode 100644 index 00000000..c0196e5f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/ScreenAccessor.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.mixin.accessor; + +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.Text; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Screen.class) +public interface ScreenAccessor { + @Accessor + @Mutable + void setTitle(Text title); +} diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/WorldRendererAccessor.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/WorldRendererAccessor.java new file mode 100644 index 00000000..f1b3158d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/WorldRendererAccessor.java @@ -0,0 +1,13 @@ +package de.hysky.skyblocker.mixin.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.WorldRenderer; + +@Mixin(WorldRenderer.class) +public interface WorldRendererAccessor { + @Accessor + Frustum getFrustum(); +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/FairySouls.java b/src/main/java/de/hysky/skyblocker/skyblock/FairySouls.java new file mode 100644 index 00000000..24465e06 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/FairySouls.java @@ -0,0 +1,215 @@ +package de.hysky.skyblocker.skyblock; + +import com.google.common.collect.ImmutableSet; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.brigadier.CommandDispatcher; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.NEURepo; +import de.hysky.skyblocker.utils.PosUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class FairySouls { + private static final Logger LOGGER = LoggerFactory.getLogger(FairySouls.class); + private static CompletableFuture fairySoulsLoaded; + private static int maxSouls = 0; + private static final Map> fairySouls = new HashMap<>(); + private static final Map>> foundFairies = new HashMap<>(); + + @SuppressWarnings("UnusedReturnValue") + public static CompletableFuture runAsyncAfterFairySoulsLoad(Runnable runnable) { + if (fairySoulsLoaded == null) { + LOGGER.error("Fairy Souls have not being initialized yet! Please ensure the Fairy Souls module is initialized before modules calling this method in SkyblockerMod#onInitializeClient. This error can be safely ignore in a test environment."); + return CompletableFuture.completedFuture(null); + } + return fairySoulsLoaded.thenRunAsync(runnable); + } + + public static int getFairySoulsSize(@Nullable String location) { + return location == null ? maxSouls : fairySouls.get(location).size(); + } + + public static void init() { + loadFairySouls(); + ClientLifecycleEvents.CLIENT_STOPPING.register(FairySouls::saveFoundFairySouls); + ClientCommandRegistrationCallback.EVENT.register(FairySouls::registerCommands); + WorldRenderEvents.AFTER_TRANSLUCENT.register(FairySouls::render); + ClientReceiveMessageEvents.GAME.register(FairySouls::onChatMessage); + } + + private static void loadFairySouls() { + fairySoulsLoaded = NEURepo.runAsyncAfterLoad(() -> { + try (BufferedReader reader = new BufferedReader(new FileReader(NEURepo.LOCAL_REPO_DIR.resolve("constants").resolve("fairy_souls.json").toFile()))) { + for (Map.Entry fairySoulJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { + if (fairySoulJson.getKey().equals("//") || fairySoulJson.getKey().equals("Max Souls")) { + if (fairySoulJson.getKey().equals("Max Souls")) { + maxSouls = fairySoulJson.getValue().getAsInt(); + } + continue; + } + ImmutableSet.Builder fairySoulsForLocation = ImmutableSet.builder(); + for (JsonElement fairySoul : fairySoulJson.getValue().getAsJsonArray().asList()) { + fairySoulsForLocation.add(PosUtils.parsePosString(fairySoul.getAsString())); + } + fairySouls.put(fairySoulJson.getKey(), fairySoulsForLocation.build()); + } + LOGGER.debug("[Skyblocker] Loaded fairy soul locations"); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to load fairy soul locations", e); + } + + try (BufferedReader reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()))) { + for (Map.Entry foundFairiesForProfileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { + Map> foundFairiesForProfile = new HashMap<>(); + for (Map.Entry foundFairiesForLocationJson : foundFairiesForProfileJson.getValue().getAsJsonObject().asMap().entrySet()) { + Set foundFairiesForLocation = new HashSet<>(); + for (JsonElement foundFairy : foundFairiesForLocationJson.getValue().getAsJsonArray().asList()) { + foundFairiesForLocation.add(PosUtils.parsePosString(foundFairy.getAsString())); + } + foundFairiesForProfile.put(foundFairiesForLocationJson.getKey(), foundFairiesForLocation); + } + foundFairies.put(foundFairiesForProfileJson.getKey(), foundFairiesForProfile); + } + LOGGER.debug("[Skyblocker] Loaded found fairy souls"); + } catch (FileNotFoundException ignored) { + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to load found fairy souls", e); + } + }); + } + + private static void saveFoundFairySouls(MinecraftClient client) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()))) { + JsonObject foundFairiesJson = new JsonObject(); + for (Map.Entry>> foundFairiesForProfile : foundFairies.entrySet()) { + JsonObject foundFairiesForProfileJson = new JsonObject(); + for (Map.Entry> foundFairiesForLocation : foundFairiesForProfile.getValue().entrySet()) { + JsonArray foundFairiesForLocationJson = new JsonArray(); + for (BlockPos foundFairy : foundFairiesForLocation.getValue()) { + foundFairiesForLocationJson.add(PosUtils.getPosString(foundFairy)); + } + foundFairiesForProfileJson.add(foundFairiesForLocation.getKey(), foundFairiesForLocationJson); + } + foundFairiesJson.add(foundFairiesForProfile.getKey(), foundFairiesForProfileJson); + } + SkyblockerMod.GSON.toJson(foundFairiesJson, writer); + writer.close(); + LOGGER.info("[Skyblocker] Saved found fairy souls"); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to write found fairy souls to file", e); + } + } + + private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(literal(SkyblockerMod.NAMESPACE) + .then(literal("fairySouls") + .then(literal("markAllInCurrentIslandFound").executes(context -> { + FairySouls.markAllFairiesOnCurrentIslandFound(); + context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllFound")); + return 1; + })) + .then(literal("markAllInCurrentIslandMissing").executes(context -> { + FairySouls.markAllFairiesOnCurrentIslandMissing(); + context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllMissing")); + return 1; + })))); + } + + private static void render(WorldRenderContext context) { + SkyblockerConfig.FairySouls fairySoulsConfig = SkyblockerConfigManager.get().general.fairySouls; + + if (fairySoulsConfig.enableFairySoulsHelper && fairySoulsLoaded.isDone() && fairySouls.containsKey(Utils.getLocationRaw())) { + for (BlockPos fairySoulPos : fairySouls.get(Utils.getLocationRaw())) { + boolean fairySoulNotFound = isFairySoulMissing(fairySoulPos); + if (!fairySoulsConfig.highlightFoundSouls && !fairySoulNotFound || fairySoulsConfig.highlightOnlyNearbySouls && fairySoulPos.getSquaredDistance(context.camera().getPos()) > 2500) { + continue; + } + float[] colorComponents = fairySoulNotFound ? DyeColor.GREEN.getColorComponents() : DyeColor.RED.getColorComponents(); + RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoulPos, colorComponents, 0.5F); + } + } + } + + private static void onChatMessage(Text text, boolean overlay) { + String message = text.getString(); + if (message.equals("You have already found that Fairy Soul!") || message.equals("§d§lSOUL! §fYou found a §dFairy Soul§f!")) { + markClosestFairyFound(); + } + } + + private static void markClosestFairyFound() { + if (!fairySoulsLoaded.isDone()) return; + PlayerEntity player = MinecraftClient.getInstance().player; + if (player == null) { + LOGGER.warn("[Skyblocker] Failed to mark closest fairy soul as found because player is null"); + return; + } + fairySouls.get(Utils.getLocationRaw()).stream() + .filter(FairySouls::isFairySoulMissing) + .min(Comparator.comparingDouble(fairySoulPos -> fairySoulPos.getSquaredDistance(player.getPos()))) + .filter(fairySoulPos -> fairySoulPos.getSquaredDistance(player.getPos()) <= 16) + .ifPresent(fairySoulPos -> { + initializeFoundFairiesForCurrentProfileAndLocation(); + foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).add(fairySoulPos); + }); + } + + private static boolean isFairySoulMissing(BlockPos fairySoulPos) { + Map> foundFairiesForProfile = foundFairies.get(Utils.getProfile()); + if (foundFairiesForProfile == null) { + return true; + } + Set foundFairiesForProfileAndLocation = foundFairiesForProfile.get(Utils.getLocationRaw()); + if (foundFairiesForProfileAndLocation == null) { + return true; + } + return !foundFairiesForProfileAndLocation.contains(fairySoulPos); + } + + public static void markAllFairiesOnCurrentIslandFound() { + initializeFoundFairiesForCurrentProfileAndLocation(); + foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).addAll(fairySouls.get(Utils.getLocationRaw())); + } + + public static void markAllFairiesOnCurrentIslandMissing() { + Map> foundFairiesForProfile = foundFairies.get(Utils.getProfile()); + if (foundFairiesForProfile != null) { + foundFairiesForProfile.remove(Utils.getLocationRaw()); + } + } + + private static void initializeFoundFairiesForCurrentProfileAndLocation() { + initializeFoundFairiesForProfileAndLocation(Utils.getProfile(), Utils.getLocationRaw()); + } + + private static void initializeFoundFairiesForProfileAndLocation(String profile, String location) { + foundFairies.computeIfAbsent(profile, profileKey -> new HashMap<>()); + foundFairies.get(profile).computeIfAbsent(location, locationKey -> new HashSet<>()); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java b/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java new file mode 100644 index 00000000..4cd356a8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/FancyStatusBars.java @@ -0,0 +1,192 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.util.Identifier; + +public class FancyStatusBars { + private static final Identifier BARS = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/bars.png"); + + private final MinecraftClient client = MinecraftClient.getInstance(); + private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker; + + private final StatusBar[] bars = new StatusBar[]{ + new StatusBar(0, 16733525, 2), // Health Bar + new StatusBar(1, 5636095, 2), // Intelligence Bar + new StatusBar(2, 12106180, 1), // Defence Bar + new StatusBar(3, 8453920, 1), // Experience Bar + }; + + // Positions to show the bars + // 0: Hotbar Layer 1, 1: Hotbar Layer 2, 2: Right of hotbar + // Anything outside the set values hides the bar + private final int[] anchorsX = new int[3]; + private final int[] anchorsY = new int[3]; + + public FancyStatusBars() { + moveBar(0, 0); + moveBar(1, 0); + moveBar(2, 0); + moveBar(3, 0); + } + + private int fill(int value, int max) { + return (100 * value) / max; + } + + public boolean render(DrawContext context, int scaledWidth, int scaledHeight) { + var player = client.player; + if (!SkyblockerConfigManager.get().general.bars.enableBars || player == null || Utils.isInTheRift()) + return false; + anchorsX[0] = scaledWidth / 2 - 91; + anchorsY[0] = scaledHeight - 33; + anchorsX[1] = anchorsX[0]; + anchorsY[1] = anchorsY[0] - 10; + anchorsX[2] = (scaledWidth / 2 + 91) + 2; + anchorsY[2] = scaledHeight - 16; + + bars[0].update(statusBarTracker.getHealth()); + bars[1].update(statusBarTracker.getMana()); + int def = statusBarTracker.getDefense(); + bars[2].fill[0] = fill(def, def + 100); + bars[2].text = def; + bars[3].fill[0] = (int) (100 * player.experienceProgress); + bars[3].text = player.experienceLevel; + + // Update positions of bars from config + for (int i = 0; i < 4; i++) { + int configAnchorNum = switch (i) { + case 0 -> SkyblockerConfigManager.get().general.bars.barPositions.healthBarPosition.toInt(); + case 1 -> SkyblockerConfigManager.get().general.bars.barPositions.manaBarPosition.toInt(); + case 2 -> SkyblockerConfigManager.get().general.bars.barPositions.defenceBarPosition.toInt(); + case 3 -> SkyblockerConfigManager.get().general.bars.barPositions.experienceBarPosition.toInt(); + default -> 0; + }; + + if (bars[i].anchorNum != configAnchorNum) + moveBar(i, configAnchorNum); + } + + for (var bar : bars) { + bar.draw(context); + } + for (var bar : bars) { + bar.drawText(context); + } + return true; + } + + public void moveBar(int bar, int location) { + // Set the bar to the new anchor + bars[bar].anchorNum = location; + + // Count how many bars are in each location + int layer1Count = 0, layer2Count = 0, rightCount = 0; + for (int i = 0; i < 4; i++) { + switch (bars[i].anchorNum) { + case 0 -> layer1Count++; + case 1 -> layer2Count++; + case 2 -> rightCount++; + } + } + + // Set the bars width and offsetX according to their anchor and how many bars are on that layer + int adjustedLayer1Count = 0, adjustedLayer2Count = 0, adjustedRightCount = 0; + for (int i = 0; i < 4; i++) { + switch (bars[i].anchorNum) { + case 0 -> { + bars[i].bar_width = (172 - ((layer1Count - 1) * 11)) / layer1Count; + bars[i].offsetX = adjustedLayer1Count * (bars[i].bar_width + 11 + (layer1Count == 3 ? 0 : 1)); + adjustedLayer1Count++; + } + case 1 -> { + bars[i].bar_width = (172 - ((layer2Count - 1) * 11)) / layer2Count; + bars[i].offsetX = adjustedLayer2Count * (bars[i].bar_width + 11 + (layer2Count == 3 ? 0 : 1)); + adjustedLayer2Count++; + } + case 2 -> { + bars[i].bar_width = 50; + bars[i].offsetX = adjustedRightCount * (50 + 11); + adjustedRightCount++; + } + } + } + } + + private class StatusBar { + public final int[] fill; + public int offsetX; + private final int v; + private final int text_color; + public int anchorNum; + public int bar_width; + public Object text; + + private StatusBar(int i, int textColor, int fillNum) { + this.v = i * 9; + this.text_color = textColor; + this.fill = new int[fillNum]; + this.fill[0] = 100; + this.anchorNum = 0; + this.text = ""; + } + + public void update(StatusBarTracker.Resource resource) { + int max = resource.max(); + int val = resource.value(); + this.fill[0] = fill(val, max); + this.fill[1] = fill(resource.overflow(), max); + this.text = val; + } + + public void draw(DrawContext context) { + // Dont draw if anchorNum is outside of range + if (anchorNum < 0 || anchorNum > 2) return; + + // Draw the icon for the bar + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX, anchorsY[anchorNum], 0, v, 9, 9); + + // Draw the background for the bar + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10, anchorsY[anchorNum], 10, v, 2, 9); + for (int i = 2; i < bar_width - 2; i += 58) { + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + i, anchorsY[anchorNum], 12, v, Math.min(58, bar_width - 2 - i), 9); + } + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + bar_width - 2, anchorsY[anchorNum], 70, v, 2, 9); + + // Draw the filled part of the bar + for (int i = 0; i < fill.length; i++) { + int fill_width = this.fill[i] * (bar_width - 2) / 100; + if (fill_width >= 1) { + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum], 72 + i * 60, v, 1, 9); + } + for (int j = 1; j < fill_width - 1; j += 58) { + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + j, anchorsY[anchorNum], 73 + i * 60, v, Math.min(58, fill_width - 1 - j), 9); + } + if (fill_width == bar_width - 2) { + context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + fill_width - 1, anchorsY[anchorNum], 131 + i * 60, v, 1, 9); + } + } + } + + public void drawText(DrawContext context) { + // Dont draw if anchorNum is outside of range + if (anchorNum < 0 || anchorNum > 2) return; + + TextRenderer textRenderer = client.textRenderer; + String text = this.text.toString(); + int x = anchorsX[anchorNum] + this.offsetX + 11 + (bar_width - textRenderer.getWidth(text)) / 2; + int y = anchorsY[anchorNum] - 3; + + final int[] offsets = new int[]{-1, 1}; + for (int i : offsets) { + context.drawText(textRenderer, text, x + i, y, 0, false); + context.drawText(textRenderer, text, x, y + i, 0, false); + } + context.drawText(textRenderer, text, x, y, text_color, false); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/FishingHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/FishingHelper.java new file mode 100644 index 00000000..6edb416e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/FishingHelper.java @@ -0,0 +1,62 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.title.Title; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FishingRodItem; +import net.minecraft.item.ItemStack; +import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; +import net.minecraft.util.Formatting; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class FishingHelper { + private static final Title title = new Title("skyblocker.fishing.reelNow", Formatting.GREEN); + private static long startTime; + private static Vec3d normalYawVector; + + public static void init() { + UseItemCallback.EVENT.register((player, world, hand) -> { + ItemStack stack = player.getStackInHand(hand); + if (stack.getItem() instanceof FishingRodItem) { + if (player.fishHook == null) { + start(player); + } else { + reset(); + } + } + return TypedActionResult.pass(stack); + }); + } + + public static void start(PlayerEntity player) { + startTime = System.currentTimeMillis(); + float yawRad = player.getYaw() * 0.017453292F; + normalYawVector = new Vec3d(-MathHelper.sin(yawRad), 0, MathHelper.cos(yawRad)); + } + + public static void reset() { + startTime = 0; + } + + public static void onSound(PlaySoundS2CPacket packet) { + String path = packet.getSound().value().getId().getPath(); + if (SkyblockerConfigManager.get().general.fishing.enableFishingHelper && startTime != 0 && System.currentTimeMillis() >= startTime + 2000 && ("entity.generic.splash".equals(path) || "entity.player.splash".equals(path))) { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player != null && player.fishHook != null) { + Vec3d soundToFishHook = player.fishHook.getPos().subtract(packet.getX(), 0, packet.getZ()); + if (Math.abs(normalYawVector.x * soundToFishHook.z - normalYawVector.z * soundToFishHook.x) < 0.2D && Math.abs(normalYawVector.dotProduct(soundToFishHook)) < 4D && player.getPos().squaredDistanceTo(packet.getX(), packet.getY(), packet.getZ()) > 1D) { + RenderHelper.displayInTitleContainerAndPlaySound(title, 10); + reset(); + } + } else { + reset(); + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java b/src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java new file mode 100644 index 00000000..13f09ec6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/HotbarSlotLock.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.option.KeyBinding; +import org.lwjgl.glfw.GLFW; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +public class HotbarSlotLock { + public static KeyBinding hotbarSlotLock; + + public static void init() { + hotbarSlotLock = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.hotbarSlotLock", + GLFW.GLFW_KEY_H, + "key.categories.skyblocker" + )); + } + + public static boolean isLocked(int slot) { + return SkyblockerConfigManager.get().general.lockedSlots.contains(slot); + } + + public static void handleDropSelectedItem(int slot, CallbackInfoReturnable cir) { + if (isLocked(slot)) cir.setReturnValue(false); + } + + public static void handleInputEvents(ClientPlayerEntity player) { + while (hotbarSlotLock.wasPressed()) { + List lockedSlots = SkyblockerConfigManager.get().general.lockedSlots; + int selected = player.getInventory().selectedSlot; + if (!isLocked(player.getInventory().selectedSlot)) lockedSlots.add(selected); + else lockedSlots.remove(Integer.valueOf(selected)); + SkyblockerConfigManager.save(); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/QuiverWarning.java b/src/main/java/de/hysky/skyblocker/skyblock/QuiverWarning.java new file mode 100644 index 00000000..a6c45d21 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/QuiverWarning.java @@ -0,0 +1,66 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.hud.InGameHud; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; + +public class QuiverWarning { + @Nullable + private static Type warning = null; + + public static void init() { + ClientReceiveMessageEvents.ALLOW_GAME.register(QuiverWarning::onChatMessage); + Scheduler.INSTANCE.scheduleCyclic(QuiverWarning::update, 10); + } + + public static boolean onChatMessage(Text text, boolean overlay) { + String message = text.getString(); + if (SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarning && message.endsWith("left in your Quiver!")) { + MinecraftClient.getInstance().inGameHud.setDefaultTitleFade(); + if (message.startsWith("You only have 50")) { + onChatMessage(Type.FIFTY_LEFT); + } else if (message.startsWith("You only have 10")) { + onChatMessage(Type.TEN_LEFT); + } else if (message.startsWith("You don't have any more")) { + onChatMessage(Type.EMPTY); + } + } + return true; + } + + private static void onChatMessage(Type warning) { + if (!Utils.isInDungeons()) { + MinecraftClient.getInstance().inGameHud.setTitle(Text.translatable(warning.key).formatted(Formatting.RED)); + } else if (SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarningInDungeons) { + MinecraftClient.getInstance().inGameHud.setTitle(Text.translatable(warning.key).formatted(Formatting.RED)); + QuiverWarning.warning = warning; + } + } + + public static void update() { + if (warning != null && SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarning && SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarningAfterDungeon && !Utils.isInDungeons()) { + InGameHud inGameHud = MinecraftClient.getInstance().inGameHud; + inGameHud.setDefaultTitleFade(); + inGameHud.setTitle(Text.translatable(warning.key).formatted(Formatting.RED)); + warning = null; + } + } + + private enum Type { + NONE(""), + FIFTY_LEFT("50Left"), + TEN_LEFT("10Left"), + EMPTY("empty"); + private final String key; + + Type(String key) { + this.key = "skyblocker.quiverWarning." + key; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/StatusBarTracker.java b/src/main/java/de/hysky/skyblocker/skyblock/StatusBarTracker.java new file mode 100644 index 00000000..c3483102 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/StatusBarTracker.java @@ -0,0 +1,109 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.text.Text; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class StatusBarTracker { + private static final Pattern STATUS_HEALTH = Pattern.compile("§[6c](\\d+(,\\d\\d\\d)*)/(\\d+(,\\d\\d\\d)*)❤(?:(\\+§c(\\d+(,\\d\\d\\d)*). *)| *)"); + private static final Pattern DEFENSE_STATUS = Pattern.compile("§a(\\d+(,\\d\\d\\d)*)§a❈ Defense *"); + private static final Pattern MANA_USE = Pattern.compile("§b-(\\d+(,\\d\\d\\d)*) Mana \\(§\\S+(?:\\s\\S+)* *"); + private static final Pattern MANA_STATUS = Pattern.compile("§b(\\d+(,\\d\\d\\d)*)/(\\d+(,\\d\\d\\d)*)✎ (?:Mana|§3(\\d+(,\\d\\d\\d)*)ʬ) *"); + + private Resource health = new Resource(100, 100, 0); + private Resource mana = new Resource(100, 100, 0); + private int defense = 0; + + public void init() { + ClientReceiveMessageEvents.MODIFY_GAME.register(this::onOverlayMessage); + } + + public Resource getHealth() { + return this.health; + } + + public Resource getMana() { + return this.mana; + } + + public int getDefense() { + return this.defense; + } + + private int parseInt(Matcher m, int group) { + return Integer.parseInt(m.group(group).replace(",", "")); + } + + private void updateMana(Matcher m) { + int value = parseInt(m, 1); + int max = parseInt(m, 3); + int overflow = m.group(5) == null ? 0 : parseInt(m, 5); + this.mana = new Resource(value, max, overflow); + } + + private void updateHealth(Matcher m) { + int value = parseInt(m, 1); + int max = parseInt(m, 3); + int overflow = Math.max(0, value - max); + if (MinecraftClient.getInstance() != null && MinecraftClient.getInstance().player != null) { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + value = (int) (player.getHealth() * max / player.getMaxHealth()); + overflow = (int) (player.getAbsorptionAmount() * max / player.getMaxHealth()); + } + this.health = new Resource(Math.min(value, max), max, Math.min(overflow, max)); + } + + private String reset(String str, Matcher m) { + str = str.substring(m.end()); + m.reset(str); + return str; + } + + private Text onOverlayMessage(Text text, boolean overlay) { + if (!overlay || !Utils.isOnSkyblock() || !SkyblockerConfigManager.get().general.bars.enableBars || Utils.isInTheRift()) { + return text; + } + return Text.of(update(text.getString(), SkyblockerConfigManager.get().messages.hideMana)); + } + + public String update(String actionBar, boolean filterManaUse) { + var sb = new StringBuilder(); + Matcher matcher = STATUS_HEALTH.matcher(actionBar); + if (!matcher.lookingAt()) + return actionBar; + updateHealth(matcher); + if (matcher.group(5) != null) { + sb.append("§c❤"); + sb.append(matcher.group(5)); + } + actionBar = reset(actionBar, matcher); + if (matcher.usePattern(MANA_STATUS).lookingAt()) { + defense = 0; + updateMana(matcher); + actionBar = reset(actionBar, matcher); + } else { + if (matcher.usePattern(DEFENSE_STATUS).lookingAt()) { + defense = parseInt(matcher, 1); + actionBar = reset(actionBar, matcher); + } else if (filterManaUse && matcher.usePattern(MANA_USE).lookingAt()) { + actionBar = reset(actionBar, matcher); + } + if (matcher.usePattern(MANA_STATUS).find()) { + updateMana(matcher); + matcher.appendReplacement(sb, ""); + } + } + matcher.appendTail(sb); + String res = sb.toString().trim(); + return res.isEmpty() ? null : res; + } + + public record Resource(int value, int max, int overflow) { + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java b/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java new file mode 100644 index 00000000..e878d108 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java @@ -0,0 +1,114 @@ +package de.hysky.skyblocker.skyblock; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.block.BlockState; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.hit.HitResult; +import net.minecraft.util.math.BlockPos; + +public class TeleportOverlay { + private static final float[] COLOR_COMPONENTS = {118f / 255f, 21f / 255f, 148f / 255f}; + private static final MinecraftClient client = MinecraftClient.getInstance(); + + public static void init() { + WorldRenderEvents.AFTER_TRANSLUCENT.register(TeleportOverlay::render); + } + + private static void render(WorldRenderContext wrc) { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.teleportOverlay.enableTeleportOverlays && client.player != null && client.world != null) { + ItemStack heldItem = client.player.getMainHandStack(); + String itemId = PriceInfoTooltip.getInternalNameFromNBT(heldItem, true); + NbtCompound nbt = heldItem.getNbt(); + + if (itemId != null) { + switch (itemId) { + case "ASPECT_OF_THE_LEECH_1" -> { + if (SkyblockerConfigManager.get().general.teleportOverlay.enableWeirdTransmission) { + render(wrc, 3); + } + } + case "ASPECT_OF_THE_LEECH_2" -> { + if (SkyblockerConfigManager.get().general.teleportOverlay.enableWeirdTransmission) { + render(wrc, 4); + } + } + case "ASPECT_OF_THE_END", "ASPECT_OF_THE_VOID" -> { + if (SkyblockerConfigManager.get().general.teleportOverlay.enableEtherTransmission && client.options.sneakKey.isPressed() && nbt != null && nbt.getCompound("ExtraAttributes").getInt("ethermerge") == 1) { + render(wrc, nbt, 57); + } else if (SkyblockerConfigManager.get().general.teleportOverlay.enableInstantTransmission) { + render(wrc, nbt, 8); + } + } + case "ETHERWARP_CONDUIT" -> { + if (SkyblockerConfigManager.get().general.teleportOverlay.enableEtherTransmission) { + render(wrc, nbt, 57); + } + } + case "SINSEEKER_SCYTHE" -> { + if (SkyblockerConfigManager.get().general.teleportOverlay.enableSinrecallTransmission) { + render(wrc, nbt, 4); + } + } + case "NECRON_BLADE", "ASTRAEA", "HYPERION", "SCYLLA", "VALKYRIE" -> { + if (SkyblockerConfigManager.get().general.teleportOverlay.enableWitherImpact) { + render(wrc, 10); + } + } + } + } + } + } + + /** + * Renders the teleport overlay with a given base range and the tuned transmission stat. + */ + private static void render(WorldRenderContext wrc, NbtCompound nbt, int baseRange) { + render(wrc, nbt != null && nbt.getCompound("ExtraAttributes").contains("tuned_transmission") ? baseRange + nbt.getCompound("ExtraAttributes").getInt("tuned_transmission") : baseRange); + } + + /** + * Renders the teleport overlay with a given range. Uses {@link MinecraftClient#crosshairTarget} if it is a block and within range. Otherwise, raycasts from the player with the given range. + * + * @implNote {@link MinecraftClient#player} and {@link MinecraftClient#world} must not be null when calling this method. + */ + private static void render(WorldRenderContext wrc, int range) { + if (client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.BLOCK && client.crosshairTarget instanceof BlockHitResult blockHitResult && client.crosshairTarget.squaredDistanceTo(client.player) < range * range) { + render(wrc, blockHitResult); + } else if (client.interactionManager != null && range > client.interactionManager.getReachDistance()) { + @SuppressWarnings("DataFlowIssue") + HitResult result = client.player.raycast(range, wrc.tickDelta(), false); + if (result.getType() == HitResult.Type.BLOCK && result instanceof BlockHitResult blockHitResult) { + render(wrc, blockHitResult); + } + } + } + + /** + * Renders the teleport overlay at the given {@link BlockHitResult}. + * + * @implNote {@link MinecraftClient#world} must not be null when calling this method. + */ + private static void render(WorldRenderContext wrc, BlockHitResult blockHitResult) { + BlockPos pos = blockHitResult.getBlockPos(); + @SuppressWarnings("DataFlowIssue") + BlockState state = client.world.getBlockState(pos); + if (!state.isAir() && client.world.getBlockState(pos.up()).isAir() && client.world.getBlockState(pos.up(2)).isAir()) { + RenderSystem.polygonOffset(-1f, -10f); + RenderSystem.enablePolygonOffset(); + + RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); + + RenderSystem.polygonOffset(0f, 0f); + RenderSystem.disablePolygonOffset(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/barn/HungryHiker.java b/src/main/java/de/hysky/skyblocker/skyblock/barn/HungryHiker.java new file mode 100644 index 00000000..abb4a76d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/barn/HungryHiker.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.barn; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; + +public class HungryHiker extends ChatPatternListener { + + private static final Map foods; + + public HungryHiker() { super("^§e\\[NPC] Hungry Hiker§f: (The food I want is|(I asked for) food that is) ([a-zA-Z, '\\-]*\\.)$"); } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().locations.barn.solveHungryHiker ? ChatFilterResult.FILTER : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null) return false; + String foodDescription = matcher.group(3); + String food = foods.get(foodDescription); + if (food == null) return false; + String middlePartOfTheMessageToSend = matcher.group(2) != null ? matcher.group(2) : matcher.group(1); + client.player.sendMessage(Text.of("§e[NPC] Hungry Hiker§f: " + middlePartOfTheMessageToSend + " " + food + "."), false); + return true; + } + + static { + foods = new HashMap<>(); + foods.put("from a cow.", Text.translatable("item.minecraft.cooked_beef").getString()); + foods.put("meat from a fowl.", Text.translatable("item.minecraft.cooked_chicken").getString()); + foods.put("red on the inside, green on the outside.", Text.translatable("item.minecraft.melon_slice").getString()); + foods.put("a cooked potato.", Text.translatable("item.minecraft.baked_potato").getString()); + foods.put("a stew.", Text.translatable("item.minecraft.rabbit_stew").getString()); + foods.put("a grilled meat.", Text.translatable("item.minecraft.cooked_porkchop").getString()); + foods.put("red and crunchy.", Text.translatable("item.minecraft.apple").getString()); + foods.put("made of wheat.", Text.translatable("item.minecraft.bread").getString()); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/barn/TreasureHunter.java b/src/main/java/de/hysky/skyblocker/skyblock/barn/TreasureHunter.java new file mode 100644 index 00000000..191014d5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/barn/TreasureHunter.java @@ -0,0 +1,61 @@ +package de.hysky.skyblocker.skyblock.barn; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; + +public class TreasureHunter extends ChatPatternListener { + + private static final Map locations; + + public TreasureHunter() { super("^§e\\[NPC] Treasure Hunter§f: ([a-zA-Z, '\\-\\.]*)$"); } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().locations.barn.solveTreasureHunter ? ChatFilterResult.FILTER : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null) return false; + String hint = matcher.group(1); + String location = locations.get(hint); + if (location == null) return false; + client.player.sendMessage(Text.of("§e[NPC] Treasure Hunter§f: Go mine around " + location + "."), false); + return true; + } + + static { + locations = new HashMap<>(); + locations.put("There's a treasure chest somewhere in a small cave in the gorge.", "258 70 -492"); + locations.put("I was in the desert earlier, and I saw something near a red sand rock.", "357 82 -319"); + locations.put("There's this guy who collects animals to experiment on, I think I saw something near his house.", "259 184 -564"); + locations.put("There's a small house in the gorge, I saw some treasure near there.", "297 87 -562"); + locations.put("There's this guy who says he has the best sheep in the world. I think I saw something around his hut.", "392 85 -372"); + locations.put("I spotted something by an odd looking mushroom on one of the ledges in the Mushroom Gorge, you should check it out.", "305 73 -557"); + locations.put("There are some small ruins out in the desert, might want to check them out.", "320 102 -471"); + locations.put("Some dirt was kicked up by the water pool in the overgrown Mushroom Cave. Have a look over there.", "234 56 -410"); + locations.put("There are some old stone structures in the Mushroom Gorge, give them a look.", "223 54 -503"); + locations.put("In the Mushroom Gorge where blue meets the ceiling and floor, you will find what you are looking for.", "205 42 -527"); + locations.put("There was a haystack with a crop greener than usual around it, I think there is something near there.", "334 82 -389"); + locations.put("There's a single piece of tall grass growing in the desert, I saw something there.", "283 76 -363"); + locations.put("I saw some treasure by a cow skull near the village.", "141 77 -397"); + locations.put("Near a melon patch inside a tunnel in the mountain I spotted something.", "257 100 -569"); + locations.put("I saw something near a farmer's cart, you should check it out.", "155 90 -591"); + locations.put("I remember there was a stone pillar made only of cobblestone in the oasis, could be something there.", "122 66 -409"); + locations.put("I thought I saw something near the smallest stone pillar in the oasis.", "94 65 -455"); + locations.put("I found something by a mossy stone pillar in the oasis, you should take a look.", "179 93 -537"); + locations.put("Down in the glowing Mushroom Cave, there was a weird looking mushroom, check it out.", "182 44 -451"); + locations.put("Something caught my eye by the red sand near the bridge over the gorge.", "306 105 -489"); + locations.put("I seem to recall seeing something near the well in the village.", "170 77 -375"); + locations.put("I was down near the lower oasis yesterday, I think I saw something under the bridge.", "142 69 -448"); + locations.put("I was at the upper oasis today, I recall seeing something on the cobblestone stepping stones.", "188 77 -459"); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java new file mode 100644 index 00000000..e95b47c9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CroesusHelper.java @@ -0,0 +1,34 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class CroesusHelper extends ContainerSolver { + + public CroesusHelper() { + super("^Croesus$"); + } + + @Override + protected boolean isEnabled() { + return SkyblockerConfigManager.get().locations.dungeons.croesusHelper; + } + + @Override + protected List getColors(String[] groups, Map slots) { + List highlights = new ArrayList<>(); + for (Map.Entry entry : slots.entrySet()) { + ItemStack stack = entry.getValue(); + if (stack != null && stack.getNbt() != null && (stack.getNbt().toString().contains("No more Chests to open!") || stack.getNbt().toString().contains("Opened Chest:"))) { + highlights.add(ColorHighlight.gray(entry.getKey())); + } + } + return highlights; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java new file mode 100644 index 00000000..9f247668 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java @@ -0,0 +1,152 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +/** + * This class provides functionality to render outlines around Blaze entities + */ +public class DungeonBlaze { + private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName()); + private static final float[] GREEN_COLOR_COMPONENTS = {0.0F, 1.0F, 0.0F}; + private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f}; + + private static ArmorStandEntity highestBlaze = null; + private static ArmorStandEntity lowestBlaze = null; + private static ArmorStandEntity nextHighestBlaze = null; + private static ArmorStandEntity nextLowestBlaze = null; + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(DungeonBlaze::update, 4); + WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer); + } + + /** + * Updates the state of Blaze entities and triggers the rendering process if necessary. + */ + public static void update() { + ClientWorld world = MinecraftClient.getInstance().world; + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (world == null || player == null || !Utils.isInDungeons()) return; + List> blazes = getBlazesInWorld(world, player); + sortBlazes(blazes); + updateBlazeEntities(blazes); + } + + /** + * Retrieves Blaze entities in the world and parses their health information. + * + * @param world The client world to search for Blaze entities. + * @return A list of Blaze entities and their associated health. + */ + private static List> getBlazesInWorld(ClientWorld world, ClientPlayerEntity player) { + List> blazes = new ArrayList<>(); + for (ArmorStandEntity blaze : world.getEntitiesByClass(ArmorStandEntity.class, player.getBoundingBox().expand(500D), EntityPredicates.NOT_MOUNTED)) { + String blazeName = blaze.getName().getString(); + if (blazeName.contains("Blaze") && blazeName.contains("/")) { + try { + int health = Integer.parseInt(blazeName.substring(blazeName.indexOf("/") + 1, blazeName.length() - 1)); + blazes.add(ObjectIntPair.of(blaze, health)); + } catch (NumberFormatException e) { + handleException(e); + } + } + } + return blazes; + } + + /** + * Sorts the Blaze entities based on their health values. + * + * @param blazes The list of Blaze entities to be sorted. + */ + private static void sortBlazes(List> blazes) { + blazes.sort(Comparator.comparingInt(ObjectIntPair::rightInt)); + } + + /** + * Updates information about Blaze entities based on sorted list. + * + * @param blazes The sorted list of Blaze entities with associated health values. + */ + private static void updateBlazeEntities(List> blazes) { + if (!blazes.isEmpty()) { + lowestBlaze = blazes.get(0).left(); + int highestIndex = blazes.size() - 1; + highestBlaze = blazes.get(highestIndex).left(); + if (blazes.size() > 1) { + nextLowestBlaze = blazes.get(1).left(); + nextHighestBlaze = blazes.get(highestIndex - 1).left(); + } + } + } + + /** + * Renders outlines for Blaze entities based on health and position. + * + * @param wrc The WorldRenderContext used for rendering. + */ + public static void blazeRenderer(WorldRenderContext wrc) { + try { + if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfigManager.get().locations.dungeons.blazesolver) { + if (highestBlaze.getY() < 69) { + renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc); + } + if (lowestBlaze.getY() > 69) { + renderBlazeOutline(lowestBlaze, nextLowestBlaze, wrc); + } + } + } catch (Exception e) { + handleException(e); + } + } + + /** + * Renders outlines for Blaze entities and connections between them. + * + * @param blaze The Blaze entity for which to render an outline. + * @param nextBlaze The next Blaze entity for connection rendering. + * @param wrc The WorldRenderContext used for rendering. + */ + private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) { + Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); + RenderHelper.renderOutline(wrc, blazeBox, GREEN_COLOR_COMPONENTS, 5f); + + if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) { + Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); + RenderHelper.renderOutline(wrc, nextBlazeBox, WHITE_COLOR_COMPONENTS, 5f); + + Vec3d blazeCenter = blazeBox.getCenter(); + Vec3d nextBlazeCenter = nextBlazeBox.getCenter(); + + RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f); + } + } + + /** + * Handles exceptions by logging and printing stack traces. + * + * @param e The exception to handle. + */ + private static void handleException(Exception e) { + LOGGER.warn("[Skyblocker BlazeRenderer] Encountered an unknown exception", e); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java new file mode 100644 index 00000000..4e6a5240 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java @@ -0,0 +1,169 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import com.google.gson.JsonObject; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.mixin.accessor.ScreenAccessor; +import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip; +import de.hysky.skyblocker.utils.Utils; +import it.unimi.dsi.fastutil.ints.IntBooleanPair; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.GenericContainerScreenHandler; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DungeonChestProfit { + private static final Logger LOGGER = LoggerFactory.getLogger(DungeonChestProfit.class); + private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?[A-Za-z]+) Essence x(?[0-9]+)"); + private static final DecimalFormat FORMATTER = new DecimalFormat("#,###"); + + public static void init() { + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> ScreenEvents.afterTick(screen).register(screen1 -> { + if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getScreenHandler().getType() == ScreenHandlerType.GENERIC_9X6) { + ((ScreenAccessor) screen).setTitle(getChestProfit(genericContainerScreen.getScreenHandler(), screen.getTitle(), client)); + } + })); + } + + public static Text getChestProfit(GenericContainerScreenHandler handler, Text title, MinecraftClient client) { + try { + if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator && isDungeonChest(title.getString())) { + int profit = 0; + boolean hasIncompleteData = false, usedKismet = false; + List slots = handler.slots.subList(0, handler.getRows() * 9); + + //If the item stack for the "Open Reward Chest" button or the kismet button hasn't been sent to the client yet + if (slots.get(31).getStack().isEmpty() || slots.get(50).getStack().isEmpty()) return title; + + for (Slot slot : slots) { + ItemStack stack = slot.getStack(); + + if (!stack.isEmpty()) { + String name = stack.getName().getString(); + String id = PriceInfoTooltip.getInternalNameFromNBT(stack, false); + + //Regular item price + if (id != null) { + IntBooleanPair priceData = getItemPrice(id); + + if (!priceData.rightBoolean()) hasIncompleteData = true; + + //Add the item price to the profit + profit += priceData.leftInt(); + + continue; + } + + //Essence price + if (name.contains("Essence") && SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeEssence) { + Matcher matcher = ESSENCE_PATTERN.matcher(name); + + if (matcher.matches()) { + String type = matcher.group("type"); + int amount = Integer.parseInt(matcher.group("amount")); + + IntBooleanPair priceData = getItemPrice(("ESSENCE_" + type).toUpperCase()); + + if (!priceData.rightBoolean()) hasIncompleteData = true; + + //Add the price of the essence to the profit + profit += priceData.leftInt() * amount; + + continue; + } + } + + //Determine the cost of the chest + if (name.contains("Open Reward Chest")) { + String foundString = searchLoreFor(stack, client, "Coins"); + + //Incase we're searching the free chest + if (!StringUtils.isBlank(foundString)) { + profit -= Integer.parseInt(foundString.replaceAll("[^0-9]", "")); + } + + continue; + } + + //Determine if a kismet was used or not + if (name.contains("Reroll Chest")) { + usedKismet = !StringUtils.isBlank(searchLoreFor(stack, client, "You already rerolled a chest!")); + } + } + } + + if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeKismet && usedKismet) { + IntBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER"); + + if (!kismetPriceData.rightBoolean()) hasIncompleteData = true; + + profit -= kismetPriceData.leftInt(); + } + + return Text.literal(title.getString()).append(getProfitText(profit, hasIncompleteData)); + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e); + } + + return title; + } + + /** + * @return An {@link IntBooleanPair} with the {@code left int} representing the item's price, and the {@code right boolean} indicating if the price + * was based on complete data. + */ + private static IntBooleanPair getItemPrice(String id) { + JsonObject bazaarPrices = PriceInfoTooltip.getBazaarPrices(); + JsonObject lbinPrices = PriceInfoTooltip.getLBINPrices(); + + if (bazaarPrices == null || lbinPrices == null) return IntBooleanPair.of(0, false); + + if (bazaarPrices.has(id)) { + JsonObject item = bazaarPrices.get(id).getAsJsonObject(); + boolean isPriceNull = item.get("sellPrice").isJsonNull(); + + return IntBooleanPair.of(isPriceNull ? 0 : (int) item.get("sellPrice").getAsDouble(), !isPriceNull); + } + + if (lbinPrices.has(id)) { + return IntBooleanPair.of((int) lbinPrices.get(id).getAsDouble(), true); + } + + return IntBooleanPair.of(0, false); + } + + /** + * Searches for a specific string of characters in the name and lore of an item + */ + private static String searchLoreFor(ItemStack stack, MinecraftClient client, String searchString) { + return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).filter(line -> line.contains(searchString)).findAny().orElse(null); + } + + private static boolean isDungeonChest(String name) { + return name.equals("Wood Chest") || name.equals("Gold Chest") || name.equals("Diamond Chest") || name.equals("Emerald Chest") || name.equals("Obsidian Chest") || name.equals("Bedrock Chest"); + } + + private static Text getProfitText(int profit, boolean hasIncompleteData) { + SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit; + return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor); + } + + static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) { + return Text.literal((profit > 0 ? " +" : " ") + FORMATTER.format(profit)).formatted(hasIncompleteData ? incompleteColor : (Math.abs(profit) < neutralThreshold) ? neutralColor : (profit > 0) ? profitColor : lossColor); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java new file mode 100644 index 00000000..e1af85ea --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMap.java @@ -0,0 +1,61 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.render.MapRenderer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.FilledMapItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.map.MapState; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.Identifier; +import org.apache.commons.lang3.StringUtils; + +public class DungeonMap { + private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png"); + + public static void render(MatrixStack matrices) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null || client.world == null) return; + ItemStack item = client.player.getInventory().main.get(8); + NbtCompound tag = item.getNbt(); + + if (tag != null && tag.contains("map")) { + String tag2 = tag.asString(); + tag2 = StringUtils.substringBetween(tag2, "map:", "}"); + int mapid = Integer.parseInt(tag2); + VertexConsumerProvider.Immediate vertices = client.getBufferBuilders().getEffectVertexConsumers(); + MapRenderer map = client.gameRenderer.getMapRenderer(); + MapState state = FilledMapItem.getMapState(mapid, client.world); + float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; + int x = SkyblockerConfigManager.get().locations.dungeons.mapX; + int y = SkyblockerConfigManager.get().locations.dungeons.mapY; + + if (state == null) return; + matrices.push(); + matrices.translate(x, y, 0); + matrices.scale(scaling, scaling, 0f); + map.draw(matrices, vertices, mapid, state, false, 15728880); + vertices.draw(); + matrices.pop(); + } + } + + public static void renderHUDMap(DrawContext context, int x, int y) { + float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; + int size = (int) (128 * scaling); + context.drawTexture(MAP_BACKGROUND, x, y, 0, 0, size, size, size, size); + } + + public static void init() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("hud") + .then(ClientCommandManager.literal("dungeonmap") + .executes(Scheduler.queueOpenScreenCommand(DungeonMapConfigScreen::new)))))); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java new file mode 100644 index 00000000..145ee2bc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java @@ -0,0 +1,62 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.Text; + +import java.awt.*; + +public class DungeonMapConfigScreen extends Screen { + + private int hudX = SkyblockerConfigManager.get().locations.dungeons.mapX; + private int hudY = SkyblockerConfigManager.get().locations.dungeons.mapY; + private final Screen parent; + + protected DungeonMapConfigScreen() { + this(null); + } + + public DungeonMapConfigScreen(Screen parent) { + super(Text.literal("Dungeon Map Config")); + this.parent = parent; + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + renderBackground(context, mouseX, mouseY, delta); + DungeonMap.renderHUDMap(context, hudX, hudY); + context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width >> 1, height >> 1, Color.GRAY.getRGB()); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; + int size = (int) (128 * scaling); + if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + size, hudY + size) && button == 0) { + hudX = (int) Math.max(Math.min(mouseX - (size >> 1), this.width - size), 0); + hudY = (int) Math.max(Math.min(mouseY - (size >> 1), this.height - size), 0); + } + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == 1) { + hudX = 2; + hudY = 2; + } + + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public void close() { + SkyblockerConfigManager.get().locations.dungeons.mapX = hudX; + SkyblockerConfigManager.get().locations.dungeons.mapY = hudY; + SkyblockerConfigManager.save(); + this.client.setScreen(parent); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LividColor.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LividColor.java new file mode 100644 index 00000000..762a6e17 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/LividColor.java @@ -0,0 +1,42 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.math.BlockPos; + +public class LividColor { + private static int tenTicks = 0; + + public static void init() { + ClientReceiveMessageEvents.GAME.register((message, overlay) -> { + if (SkyblockerConfigManager.get().locations.dungeons.lividColor.enableLividColor && message.getString().equals("[BOSS] Livid: I respect you for making it to here, but I'll be your undoing.")) { + tenTicks = 8; + } + }); + } + + public static void update() { + MinecraftClient client = MinecraftClient.getInstance(); + if (tenTicks != 0) { + if (SkyblockerConfigManager.get().locations.dungeons.lividColor.enableLividColor && Utils.isInDungeons() && client.world != null) { + if (tenTicks == 1) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfigManager.get().locations.dungeons.lividColor.lividColorText.replace("[color]", "red")); + tenTicks = 0; + return; + } + String key = client.world.getBlockState(new BlockPos(5, 110, 42)).getBlock().getTranslationKey(); + if (key.startsWith("block.minecraft.") && key.endsWith("wool") && !key.endsWith("red_wool")) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfigManager.get().locations.dungeons.lividColor.lividColorText.replace("[color]", key.substring(16, key.length() - 5))); + tenTicks = 0; + return; + } + tenTicks--; + } else { + tenTicks = 0; + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/OldLever.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/OldLever.java new file mode 100644 index 00000000..b9b76c59 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/OldLever.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.minecraft.block.Block; +import net.minecraft.block.enums.BlockFace; +import net.minecraft.util.math.Direction; +import net.minecraft.util.shape.VoxelShape; + +public class OldLever { + protected static final VoxelShape FLOOR_SHAPE = Block.createCuboidShape(4.0D, 0.0D, 4.0D, 12.0D, 10.0D, 12.0D); + protected static final VoxelShape NORTH_SHAPE = Block.createCuboidShape(5.0D, 3.0D, 10.0D, 11.0D, 13.0D, 16.0D); + protected static final VoxelShape SOUTH_SHAPE = Block.createCuboidShape(5.0D, 3.0D, 0.0D, 11.0D, 13.0D, 6.0D); + protected static final VoxelShape EAST_SHAPE = Block.createCuboidShape(0.0D, 3.0D, 5.0D, 6.0D, 13.0D, 11.0D); + protected static final VoxelShape WEST_SHAPE = Block.createCuboidShape(10.0D, 3.0D, 5.0D, 16.0D, 13.0D, 11.0D); + + public static VoxelShape getShape(BlockFace face, Direction direction) { + if (!SkyblockerConfigManager.get().general.hitbox.oldLeverHitbox) + return null; + + if (face == BlockFace.FLOOR) { + return FLOOR_SHAPE; + } else if (face == BlockFace.WALL) { + switch (direction) { + case EAST -> { + return EAST_SHAPE; + } + case WEST -> { + return WEST_SHAPE; + } + case SOUTH -> { + return SOUTH_SHAPE; + } + case NORTH -> { + return NORTH_SHAPE; + } + } + } + return null; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Reparty.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Reparty.java new file mode 100644 index 00000000..6165ac6a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Reparty.java @@ -0,0 +1,94 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.text.Text; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Reparty extends ChatPatternListener { + private static final MinecraftClient client = MinecraftClient.getInstance(); + public static final Pattern PLAYER = Pattern.compile(" ([a-zA-Z0-9_]{2,16}) ●"); + private static final int BASE_DELAY = 10; + + private String[] players; + private int playersSoFar; + private boolean repartying; + private String partyLeader; + + public Reparty() { + super("^(?:You are not currently in a party\\." + + "|Party (?:Membe|Moderato)rs(?: \\(([0-9]+)\\)|:( .*))" + + "|([\\[A-z+\\]]* )?(?.*) has disbanded .*" + + "|.*\n([\\[A-z+\\]]* )?(?.*) has invited you to join their party!" + + "\nYou have 60 seconds to accept. Click here to join!\n.*)$"); + + this.repartying = false; + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("rp").executes(context -> { + if (!Utils.isOnSkyblock() || this.repartying || client.player == null) return 0; + this.repartying = true; + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/p list"); + return 0; + }))); + } + + @Override + public ChatFilterResult state() { + return (SkyblockerConfigManager.get().general.acceptReparty || this.repartying) ? ChatFilterResult.FILTER : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + if (matcher.group(1) != null && repartying) { + this.playersSoFar = 0; + this.players = new String[Integer.parseInt(matcher.group(1)) - 1]; + } else if (matcher.group(2) != null && repartying) { + Matcher m = PLAYER.matcher(matcher.group(2)); + while (m.find()) { + this.players[playersSoFar++] = m.group(1); + } + } else if (matcher.group("disband") != null && !matcher.group("disband").equals(client.getSession().getUsername())) { + partyLeader = matcher.group("disband"); + Scheduler.INSTANCE.schedule(() -> partyLeader = null, 61); + return false; + } else if (matcher.group("invite") != null && matcher.group("invite").equals(partyLeader)) { + String command = "/party accept " + partyLeader; + sendCommand(command, 0); + return false; + } else { + this.repartying = false; + return false; + } + if (this.playersSoFar == this.players.length) { + reparty(); + } + return false; + } + + private void reparty() { + ClientPlayerEntity playerEntity = client.player; + if (playerEntity == null) { + this.repartying = false; + return; + } + sendCommand("/p disband", 1); + for (int i = 0; i < this.players.length; ++i) { + String command = "/p invite " + this.players[i]; + sendCommand(command, i + 2); + } + Scheduler.INSTANCE.schedule(() -> this.repartying = false, this.players.length + 2); + } + + private void sendCommand(String command, int delay) { + MessageScheduler.INSTANCE.queueMessage(command, delay * BASE_DELAY); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/StarredMobGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/StarredMobGlow.java new file mode 100644 index 00000000..2072017d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/StarredMobGlow.java @@ -0,0 +1,56 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.culling.OcclusionCulling; +import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.passive.BatEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.util.math.Box; + +import java.util.List; + +public class StarredMobGlow { + + public static boolean shouldMobGlow(Entity entity) { + Box box = entity.getBoundingBox(); + + if (Utils.isInDungeons() && !entity.isInvisible() && OcclusionCulling.isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) { + // Minibosses + if (entity instanceof PlayerEntity) { + switch (entity.getName().getString()) { + case "Lost Adventurer", "Shadow Assassin", "Diamond Guy" -> { + return true; + } + } + } + + // Regular Mobs + if (!(entity instanceof ArmorStandEntity)) { + Box searchBox = box.expand(0, 2, 0); + List armorStands = entity.getWorld().getEntitiesByClass(ArmorStandEntity.class, searchBox, EntityPredicates.NOT_MOUNTED); + + if (!armorStands.isEmpty() && armorStands.get(0).getName().getString().contains("✯")) return true; + } + + // Bats + return entity instanceof BatEntity; + } + + return false; + } + + public static int getGlowColor(Entity entity) { + if (entity instanceof PlayerEntity) { + return switch (entity.getName().getString()) { + case "Lost Adventurer" -> 0xfee15c; + case "Shadow Assassin" -> 0x5b2cb2; + case "Diamond Guy" -> 0x57c2f7; + default -> 0xf57738; + }; + } + + return 0xf57738; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java new file mode 100644 index 00000000..e1ab2fa8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.regex.Matcher; + +public class ThreeWeirdos extends ChatPatternListener { + public ThreeWeirdos() { + super("^§e\\[NPC] §c([A-Z][a-z]+)§f: (?:The reward is(?: not in my chest!|n't in any of our chests\\.)|My chest (?:doesn't have the reward\\. We are all telling the truth\\.|has the reward and I'm telling the truth!)|At least one of them is lying, and the reward is not in §c§c[A-Z][a-z]+'s §rchest\\!|Both of them are telling the truth\\. Also, §c§c[A-Z][a-z]+ §rhas the reward in their chest\\!)$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().locations.dungeons.solveThreeWeirdos ? null : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null || client.world == null) return false; + client.world.getEntitiesByClass( + ArmorStandEntity.class, + client.player.getBoundingBox().expand(3), + entity -> { + Text customName = entity.getCustomName(); + return customName != null && customName.getString().equals(matcher.group(1)); + } + ).forEach( + entity -> entity.setCustomName(Text.of(Formatting.GREEN + matcher.group(1))) + ); + return false; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java new file mode 100644 index 00000000..2d56c8a0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java @@ -0,0 +1,136 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.tictactoe.TicTacToeUtils; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.decoration.ItemFrameEntity; +import net.minecraft.item.FilledMapItem; +import net.minecraft.item.map.MapState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * Thanks to Danker for a reference implementation! + */ +public class TicTacToe { + private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class); + private static final float[] RED_COLOR_COMPONENTS = {1.0F, 0.0F, 0.0F}; + private static Box nextBestMoveToMake = null; + + public static void init() { + WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer); + } + + public static void tick() { + MinecraftClient client = MinecraftClient.getInstance(); + ClientWorld world = client.world; + ClientPlayerEntity player = client.player; + + nextBestMoveToMake = null; + + if (world == null || player == null || !Utils.isInDungeons()) return; + + //Search within 21 blocks for item frames that contain maps + Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21); + List itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap); + + try { + //Only attempt to solve if its the player's turn + if (itemFramesThatHoldMaps.size() != 9 && itemFramesThatHoldMaps.size() % 2 == 1) { + char[][] board = new char[3][3]; + BlockPos leftmostRow = null; + int sign = 1; + char facing = 'X'; + + for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) { + MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt())); + + if (mapState == null) continue; + + int column = 0, row; + sign = 1; + + //Find position of the item frame relative to where it is on the tic tac toe board + if (itemFrame.getHorizontalFacing() == Direction.SOUTH || itemFrame.getHorizontalFacing() == Direction.WEST) sign = -1; + BlockPos itemFramePos = BlockPos.ofFloored(itemFrame.getX(), itemFrame.getY(), itemFrame.getZ()); + + for (int i = 2; i >= 0; i--) { + int realI = i * sign; + BlockPos blockPos = itemFramePos; + + if (itemFrame.getX() % 0.5 == 0) { + blockPos = itemFramePos.add(realI, 0, 0); + } else if (itemFrame.getZ() % 0.5 == 0) { + blockPos = itemFramePos.add(0, 0, realI); + facing = 'Z'; + } + + Block block = world.getBlockState(blockPos).getBlock(); + if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) { + leftmostRow = blockPos; + column = i; + + break; + } + } + + //Determine the row of the item frame + if (itemFrame.getY() == 72.5) { + row = 0; + } else if (itemFrame.getY() == 71.5) { + row = 1; + } else if (itemFrame.getY() == 70.5) { + row = 2; + } else { + continue; + } + + + //Get the color of the middle pixel of the map which determines whether its X or O + int middleColor = mapState.colors[8256] & 255; + + if (middleColor == 114) { + board[row][column] = 'X'; + } else if (middleColor == 33) { + board[row][column] = 'O'; + } + + int bestMove = TicTacToeUtils.getBestMove(board) - 1; + + if (leftmostRow != null) { + double drawX = facing == 'X' ? leftmostRow.getX() - sign * (bestMove % 3) : leftmostRow.getX(); + double drawY = 72 - (double) (bestMove / 3); + double drawZ = facing == 'Z' ? leftmostRow.getZ() - sign * (bestMove % 3) : leftmostRow.getZ(); + + nextBestMoveToMake = new Box(drawX, drawY, drawZ, drawX + 1, drawY + 1, drawZ + 1); + } + } + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while determining a tic tac toe solution!", e); + } + } + + private static void solutionRenderer(WorldRenderContext context) { + try { + if (SkyblockerConfigManager.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) { + RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5); + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java new file mode 100644 index 00000000..262d4a4e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java @@ -0,0 +1,100 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.FairySouls; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.regex.Matcher; + +public class Trivia extends ChatPatternListener { + private static final Map answers; + private List solutions = Collections.emptyList(); + + public Trivia() { + super("^ +(?:([A-Za-z,' ]*\\?)|§6 ([ⓐⓑⓒ]) §a([a-zA-Z0-9 ]+))$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().locations.dungeons.solveTrivia ? ChatFilterResult.FILTER : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + String riddle = matcher.group(3); + if (riddle != null) { + if (!solutions.contains(riddle)) { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player != null) + MinecraftClient.getInstance().player.sendMessage(Text.of(" " + Formatting.GOLD + matcher.group(2) + Formatting.RED + " " + riddle), false); + return player != null; + } + } else updateSolutions(matcher.group(0)); + return false; + } + + private void updateSolutions(String question) { + String trimmedQuestion = question.trim(); + if (trimmedQuestion.equals("What SkyBlock year is it?")) { + long currentTime = System.currentTimeMillis() / 1000L; + long diff = currentTime - 1560276000; + int year = (int) (diff / 446400 + 1); + solutions = Collections.singletonList("Year " + year); + } else { + solutions = Arrays.asList(answers.get(trimmedQuestion)); + } + } + + static { + answers = Collections.synchronizedMap(new HashMap<>()); + answers.put("What is the status of The Watcher?", new String[]{"Stalker"}); + answers.put("What is the status of Bonzo?", new String[]{"New Necromancer"}); + answers.put("What is the status of Scarf?", new String[]{"Apprentice Necromancer"}); + answers.put("What is the status of The Professor?", new String[]{"Professor"}); + answers.put("What is the status of Thorn?", new String[]{"Shaman Necromancer"}); + answers.put("What is the status of Livid?", new String[]{"Master Necromancer"}); + answers.put("What is the status of Sadan?", new String[]{"Necromancer Lord"}); + answers.put("What is the status of Maxor?", new String[]{"The Wither Lords"}); + answers.put("What is the status of Goldor?", new String[]{"The Wither Lords"}); + answers.put("What is the status of Storm?", new String[]{"The Wither Lords"}); + answers.put("What is the status of Necron?", new String[]{"The Wither Lords"}); + answers.put("What is the status of Maxor, Storm, Goldor and Necron?", new String[]{"The Wither Lords"}); + answers.put("Which brother is on the Spider's Den?", new String[]{"Rick"}); + answers.put("What is the name of Rick's brother?", new String[]{"Pat"}); + answers.put("What is the name of the Painter in the Hub?", new String[]{"Marco"}); + answers.put("What is the name of the person that upgrades pets?", new String[]{"Kat"}); + answers.put("What is the name of the lady of the Nether?", new String[]{"Elle"}); + answers.put("Which villager in the Village gives you a Rogue Sword?", new String[]{"Jamie"}); + answers.put("How many unique minions are there?", new String[]{"59 Minions"}); + answers.put("Which of these enemies does not spawn in the Spider's Den?", new String[]{"Zombie Spider", "Cave Spider", "Wither Skeleton", "Dashing Spooder", "Broodfather", "Night Spider"}); + answers.put("Which of these monsters only spawns at night?", new String[]{"Zombie Villager", "Ghast"}); + answers.put("Which of these is not a dragon in The End?", new String[]{"Zoomer Dragon", "Weak Dragon", "Stonk Dragon", "Holy Dragon", "Boomer Dragon", "Booger Dragon", "Older Dragon", "Elder Dragon", "Stable Dragon", "Professor Dragon"}); + FairySouls.runAsyncAfterFairySoulsLoad(() -> { + answers.put("How many total Fairy Souls are there?", getFairySoulsSizeString(null)); + answers.put("How many Fairy Souls are there in Spider's Den?", getFairySoulsSizeString("combat_1")); + answers.put("How many Fairy Souls are there in The End?", getFairySoulsSizeString("combat_3")); + answers.put("How many Fairy Souls are there in The Farming Islands?", getFairySoulsSizeString("farming_1")); + answers.put("How many Fairy Souls are there in Crimson Isle?", getFairySoulsSizeString("crimson_isle")); + answers.put("How many Fairy Souls are there in The Park?", getFairySoulsSizeString("foraging_1")); + answers.put("How many Fairy Souls are there in Jerry's Workshop?", getFairySoulsSizeString("winter")); + answers.put("How many Fairy Souls are there in Hub?", getFairySoulsSizeString("hub")); + answers.put("How many Fairy Souls are there in The Hub?", getFairySoulsSizeString("hub")); + answers.put("How many Fairy Souls are there in Deep Caverns?", getFairySoulsSizeString("mining_2")); + answers.put("How many Fairy Souls are there in Gold Mine?", getFairySoulsSizeString("mining_1")); + answers.put("How many Fairy Souls are there in Dungeon Hub?", getFairySoulsSizeString("dungeon_hub")); + }); + } + + @NotNull + private static String[] getFairySoulsSizeString(@Nullable String location) { + return new String[]{"%d Fairy Souls".formatted(FairySouls.getFairySoulsSize(location))}; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java new file mode 100644 index 00000000..259cc3f3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java @@ -0,0 +1,275 @@ +package de.hysky.skyblocker.skyblock.dungeon.secrets; + +import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.ints.IntSortedSet; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import net.minecraft.block.MapColor; +import net.minecraft.item.map.MapIcon; +import net.minecraft.item.map.MapState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.RoundingMode; +import org.joml.Vector2i; +import org.joml.Vector2ic; + +import java.util.*; + +public class DungeonMapUtils { + public static final byte BLACK_COLOR = MapColor.BLACK.getRenderColorByte(MapColor.Brightness.LOWEST); + public static final byte WHITE_COLOR = MapColor.WHITE.getRenderColorByte(MapColor.Brightness.HIGH); + + public static byte getColor(MapState map, @Nullable Vector2ic pos) { + return pos == null ? -1 : getColor(map, pos.x(), pos.y()); + } + + public static byte getColor(MapState map, int x, int z) { + if (x < 0 || z < 0 || x >= 128 || z >= 128) { + return -1; + } + return map.colors[x + (z << 7)]; + } + + public static boolean isEntranceColor(MapState map, int x, int z) { + return getColor(map, x, z) == Room.Type.ENTRANCE.color; + } + + public static boolean isEntranceColor(MapState map, @Nullable Vector2ic pos) { + return getColor(map, pos) == Room.Type.ENTRANCE.color; + } + + @Nullable + private static Vector2i getMapPlayerPos(MapState map) { + for (MapIcon icon : map.getIcons()) { + if (icon.type() == MapIcon.Type.FRAME) { + return new Vector2i((icon.x() >> 1) + 64, (icon.z() >> 1) + 64); + } + } + return null; + } + + @Nullable + public static ObjectIntPair getMapEntrancePosAndRoomSize(@NotNull MapState map) { + Vector2ic mapPos = getMapPlayerPos(map); + if (mapPos == null) { + return null; + } + Queue posToCheck = new ArrayDeque<>(); + Set checked = new HashSet<>(); + posToCheck.add(mapPos); + checked.add(mapPos); + while ((mapPos = posToCheck.poll()) != null) { + if (isEntranceColor(map, mapPos)) { + ObjectIntPair mapEntranceAndRoomSizePos = getMapEntrancePosAndRoomSizeAt(map, mapPos); + if (mapEntranceAndRoomSizePos.rightInt() > 0) { + return mapEntranceAndRoomSizePos; + } + } + Vector2ic pos = new Vector2i(mapPos).sub(10, 0); + if (checked.add(pos)) { + posToCheck.add(pos); + } + pos = new Vector2i(mapPos).sub(0, 10); + if (checked.add(pos)) { + posToCheck.add(pos); + } + pos = new Vector2i(mapPos).add(10, 0); + if (checked.add(pos)) { + posToCheck.add(pos); + } + pos = new Vector2i(mapPos).add(0, 10); + if (checked.add(pos)) { + posToCheck.add(pos); + } + } + return null; + } + + private static ObjectIntPair getMapEntrancePosAndRoomSizeAt(MapState map, Vector2ic mapPosImmutable) { + Vector2i mapPos = new Vector2i(mapPosImmutable); + // noinspection StatementWithEmptyBody + while (isEntranceColor(map, mapPos.sub(1, 0))) { + } + mapPos.add(1, 0); + //noinspection StatementWithEmptyBody + while (isEntranceColor(map, mapPos.sub(0, 1))) { + } + return ObjectIntPair.of(mapPos.add(0, 1), getMapRoomSize(map, mapPos)); + } + + public static int getMapRoomSize(MapState map, Vector2ic mapEntrancePos) { + int i = -1; + //noinspection StatementWithEmptyBody + while (isEntranceColor(map, mapEntrancePos.x() + ++i, mapEntrancePos.y())) { + } + return i > 5 ? i : 0; + } + + /** + * Gets the map position of the top left corner of the room the player is in. + * + * @param map the map + * @param mapEntrancePos the map position of the top left corner of the entrance + * @param mapRoomSize the size of a room on the map + * @return the map position of the top left corner of the room the player is in + * @implNote {@code mapPos} is shifted by 2 so room borders are evenly split. + * {@code mapPos} is then shifted by {@code offset} to align the top left most room at (0, 0) + * so subtracting the modulo will give the top left corner of the room shifted by {@code offset}. + * Finally, {@code mapPos} is shifted back by {@code offset} to its intended position. + */ + @Nullable + public static Vector2ic getMapRoomPos(MapState map, Vector2ic mapEntrancePos, int mapRoomSize) { + int mapRoomSizeWithGap = mapRoomSize + 4; + Vector2i mapPos = getMapPlayerPos(map); + if (mapPos == null) { + return null; + } + Vector2ic offset = new Vector2i(mapEntrancePos.x() % mapRoomSizeWithGap, mapEntrancePos.y() % mapRoomSizeWithGap); + return mapPos.add(2, 2).sub(offset).sub(mapPos.x() % mapRoomSizeWithGap, mapPos.y() % mapRoomSizeWithGap).add(offset); + } + + /** + * Gets the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room. + * + * @param physicalEntrancePos the physical position of the northwest corner of the entrance room + * @param mapEntrancePos the map position of the top left corner of the entrance room + * @param mapRoomSize the size of a room on the map + * @param physicalPos the physical position of the northwest corner of the room + * @return the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room + */ + public static Vector2ic getMapPosFromPhysical(Vector2ic physicalEntrancePos, Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalPos) { + return new Vector2i(physicalPos).sub(physicalEntrancePos).div(32).mul(mapRoomSize + 4).add(mapEntrancePos); + } + + /** + * @see #getPhysicalRoomPos(double, double) + */ + @NotNull + public static Vector2ic getPhysicalRoomPos(@NotNull Vec3d pos) { + return getPhysicalRoomPos(pos.getX(), pos.getZ()); + } + + /** + * @see #getPhysicalRoomPos(double, double) + */ + @NotNull + public static Vector2ic getPhysicalRoomPos(@NotNull Vec3i pos) { + return getPhysicalRoomPos(pos.getX(), pos.getZ()); + } + + /** + * Gets the physical position of the northwest corner of the room the given coordinate is in. Hypixel Skyblock Dungeons are aligned to a 32 by 32 blocks grid, allowing corners to be calculated through math. + * + * @param x the x position of the coordinate to calculate + * @param z the z position of the coordinate to calculate + * @return the physical position of the northwest corner of the room the player is in + * @implNote {@code physicalPos} is shifted by 0.5 so room borders are evenly split. + * {@code physicalPos} is further shifted by 8 because Hypixel offset dungeons by 8 blocks in Skyblock 0.12.3. + * Subtracting the modulo gives the northwest corner of the room shifted by 8. Finally, {@code physicalPos} is shifted back by 8 to its intended position. + */ + @NotNull + public static Vector2ic getPhysicalRoomPos(double x, double z) { + Vector2i physicalPos = new Vector2i(x + 8.5, z + 8.5, RoundingMode.TRUNCATE); + return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8); + } + + public static Vector2ic[] getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic... mapPositions) { + for (int i = 0; i < mapPositions.length; i++) { + mapPositions[i] = getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, mapPositions[i]); + } + return mapPositions; + } + + /** + * Gets the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room. + * + * @param mapEntrancePos the map position of the top left corner of the entrance room + * @param mapRoomSize the size of a room on the map + * @param physicalEntrancePos the physical position of the northwest corner of the entrance room + * @param mapPos the map position of the top left corner of the room + * @return the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room + */ + public static Vector2ic getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic mapPos) { + return new Vector2i(mapPos).sub(mapEntrancePos).div(mapRoomSize + 4).mul(32).add(physicalEntrancePos); + } + + public static Vector2ic getPhysicalCornerPos(Room.Direction direction, IntSortedSet segmentsX, IntSortedSet segmentsY) { + return switch (direction) { + case NW -> new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()); + case NE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.firstInt()); + case SW -> new Vector2i(segmentsX.firstInt(), segmentsY.lastInt() + 30); + case SE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.lastInt() + 30); + }; + } + + public static BlockPos actualToRelative(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) { + return switch (direction) { + case NW -> new BlockPos(pos.getX() - physicalCornerPos.x(), pos.getY(), pos.getZ() - physicalCornerPos.y()); + case NE -> new BlockPos(pos.getZ() - physicalCornerPos.y(), pos.getY(), -pos.getX() + physicalCornerPos.x()); + case SW -> new BlockPos(-pos.getZ() + physicalCornerPos.y(), pos.getY(), pos.getX() - physicalCornerPos.x()); + case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y()); + }; + } + + public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, JsonObject posJson) { + return relativeToActual(direction, physicalCornerPos, new BlockPos(posJson.get("x").getAsInt(), posJson.get("y").getAsInt(), posJson.get("z").getAsInt())); + } + + public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) { + return switch (direction) { + case NW -> new BlockPos(pos.getX() + physicalCornerPos.x(), pos.getY(), pos.getZ() + physicalCornerPos.y()); + case NE -> new BlockPos(-pos.getZ() + physicalCornerPos.x(), pos.getY(), pos.getX() + physicalCornerPos.y()); + case SW -> new BlockPos(pos.getZ() + physicalCornerPos.x(), pos.getY(), -pos.getX() + physicalCornerPos.y()); + case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y()); + }; + } + + public static Room.Type getRoomType(MapState map, Vector2ic mapPos) { + return switch (getColor(map, mapPos)) { + case 30 -> Room.Type.ENTRANCE; + case 63 -> Room.Type.ROOM; + case 66 -> Room.Type.PUZZLE; + case 62 -> Room.Type.TRAP; + case 74 -> Room.Type.MINIBOSS; + case 82 -> Room.Type.FAIRY; + case 18 -> Room.Type.BLOOD; + case 85 -> Room.Type.UNKNOWN; + default -> null; + }; + } + + public static Vector2ic[] getRoomSegments(MapState map, Vector2ic mapPos, int mapRoomSize, byte color) { + Set segments = new HashSet<>(); + Queue queue = new ArrayDeque<>(); + segments.add(mapPos); + queue.add(mapPos); + while (!queue.isEmpty()) { + Vector2ic curMapPos = queue.poll(); + Vector2i newMapPos = new Vector2i(); + if (getColor(map, newMapPos.set(curMapPos).sub(1, 0)) == color && !segments.contains(newMapPos.sub(mapRoomSize + 3, 0))) { + segments.add(newMapPos); + queue.add(newMapPos); + newMapPos = new Vector2i(); + } + if (getColor(map, newMapPos.set(curMapPos).sub(0, 1)) == color && !segments.contains(newMapPos.sub(0, mapRoomSize + 3))) { + segments.add(newMapPos); + queue.add(newMapPos); + newMapPos = new Vector2i(); + } + if (getColor(map, newMapPos.set(curMapPos).add(mapRoomSize, 0)) == color && !segments.contains(newMapPos.add(4, 0))) { + segments.add(newMapPos); + queue.add(newMapPos); + newMapPos = new Vector2i(); + } + if (getColor(map, newMapPos.set(curMapPos).add(0, mapRoomSize)) == color && !segments.contains(newMapPos.add(0, 4))) { + segments.add(newMapPos); + queue.add(newMapPos); + } + } + DungeonSecrets.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray())); + return segments.toArray(Vector2ic[]::new); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java new file mode 100644 index 00000000..7d20644a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -0,0 +1,451 @@ +package de.hysky.skyblocker.skyblock.dungeon.secrets; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import it.unimi.dsi.fastutil.objects.Object2ByteMap; +import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIntPair; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.entity.passive.BatEntity; +import net.minecraft.item.FilledMapItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.map.MapState; +import net.minecraft.resource.Resource; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Identifier; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector2ic; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.zip.InflaterInputStream; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class DungeonSecrets { + protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class); + private static final String DUNGEONS_PATH = "dungeons"; + /** + * Maps the block identifier string to a custom numeric block id used in dungeon rooms data. + * + * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft. + */ + @SuppressWarnings("JavadocReference") + protected static final Object2ByteMap NUMERIC_ID = new Object2ByteOpenHashMap<>(Map.ofEntries( + Map.entry("minecraft:stone", (byte) 1), + Map.entry("minecraft:diorite", (byte) 2), + Map.entry("minecraft:polished_diorite", (byte) 3), + Map.entry("minecraft:andesite", (byte) 4), + Map.entry("minecraft:polished_andesite", (byte) 5), + Map.entry("minecraft:grass_block", (byte) 6), + Map.entry("minecraft:dirt", (byte) 7), + Map.entry("minecraft:coarse_dirt", (byte) 8), + Map.entry("minecraft:cobblestone", (byte) 9), + Map.entry("minecraft:bedrock", (byte) 10), + Map.entry("minecraft:oak_leaves", (byte) 11), + Map.entry("minecraft:gray_wool", (byte) 12), + Map.entry("minecraft:double_stone_slab", (byte) 13), + Map.entry("minecraft:mossy_cobblestone", (byte) 14), + Map.entry("minecraft:clay", (byte) 15), + Map.entry("minecraft:stone_bricks", (byte) 16), + Map.entry("minecraft:mossy_stone_bricks", (byte) 17), + Map.entry("minecraft:chiseled_stone_bricks", (byte) 18), + Map.entry("minecraft:gray_terracotta", (byte) 19), + Map.entry("minecraft:cyan_terracotta", (byte) 20), + Map.entry("minecraft:black_terracotta", (byte) 21) + )); + /** + * Block data for dungeon rooms. See {@link de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU} for format details and how it's generated. + * All access to this map must check {@link #isRoomsLoaded()} to prevent concurrent modification. + */ + @SuppressWarnings("JavadocReference") + protected static final HashMap>> ROOMS_DATA = new HashMap<>(); + @NotNull + private static final Map rooms = new HashMap<>(); + private static final Map roomsJson = new HashMap<>(); + private static final Map waypointsJson = new HashMap<>(); + @Nullable + private static CompletableFuture roomsLoaded; + /** + * The map position of the top left corner of the entrance room. + */ + @Nullable + private static Vector2ic mapEntrancePos; + /** + * The size of a room on the map. + */ + private static int mapRoomSize; + /** + * The physical position of the northwest corner of the entrance room. + */ + @Nullable + private static Vector2ic physicalEntrancePos; + private static Room currentRoom; + + public static boolean isRoomsLoaded() { + return roomsLoaded != null && roomsLoaded.isDone(); + } + + @SuppressWarnings("unused") + public static JsonObject getRoomMetadata(String room) { + return roomsJson.get(room).getAsJsonObject(); + } + + public static JsonArray getRoomWaypoints(String room) { + return waypointsJson.get(room).getAsJsonArray(); + } + + /** + * Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}. + * Use {@link #isRoomsLoaded()} to check for completion of loading. + */ + public static void init() { + if (SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.noInitSecretWaypoints) { + return; + } + // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set + CompletableFuture.runAsync(DungeonSecrets::load, MinecraftClient.getInstance()).exceptionally(e -> { + LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e); + return null; + }); + Scheduler.INSTANCE.scheduleCyclic(DungeonSecrets::update, 10); + WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render); + ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage); + ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage); + UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> onUseBlock(world, hitResult)); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("secrets") + .then(literal("markAsFound").then(markSecretsCommand(true))) + .then(literal("markAsMissing").then(markSecretsCommand(false))))))); + ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset())); + } + + private static void load() { + long startTime = System.currentTimeMillis(); + List> dungeonFutures = new ArrayList<>(); + for (Map.Entry resourceEntry : MinecraftClient.getInstance().getResourceManager().findResources(DUNGEONS_PATH, id -> id.getPath().endsWith(".skeleton")).entrySet()) { + String[] path = resourceEntry.getKey().getPath().split("/"); + if (path.length != 4) { + LOGGER.error("[Skyblocker] Failed to load dungeon secrets, invalid resource identifier {}", resourceEntry.getKey()); + break; + } + String dungeon = path[1]; + String roomShape = path[2]; + String room = path[3].substring(0, path[3].length() - ".skeleton".length()); + ROOMS_DATA.computeIfAbsent(dungeon, dungeonKey -> new HashMap<>()); + ROOMS_DATA.get(dungeon).computeIfAbsent(roomShape, roomShapeKey -> new HashMap<>()); + dungeonFutures.add(CompletableFuture.supplyAsync(() -> readRoom(resourceEntry.getValue())).thenAcceptAsync(rooms -> { + Map roomsMap = ROOMS_DATA.get(dungeon).get(roomShape); + synchronized (roomsMap) { + roomsMap.put(room, rooms); + } + LOGGER.debug("[Skyblocker] Loaded dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room); + }).exceptionally(e -> { + LOGGER.error("[Skyblocker] Failed to load dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room, e); + return null; + })); + } + dungeonFutures.add(CompletableFuture.runAsync(() -> { + try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { + loadJson(roomsReader, roomsJson); + loadJson(waypointsReader, waypointsJson); + LOGGER.debug("[Skyblocker] Loaded dungeon secrets json"); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Failed to load dungeon secrets json", e); + } + })); + roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker] Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total in {} ms", ROOMS_DATA.size(), ROOMS_DATA.values().stream().mapToInt(Map::size).sum(), ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).mapToInt(Map::size).sum(), System.currentTimeMillis() - startTime)).exceptionally(e -> { + LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e); + return null; + }); + LOGGER.info("[Skyblocker] Started loading dungeon secrets in (blocked main thread for) {} ms", System.currentTimeMillis() - startTime); + } + + private static int[] readRoom(Resource resource) throws RuntimeException { + try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(resource.getInputStream()))) { + return (int[]) in.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Loads the json from the given {@link BufferedReader} into the given {@link Map}. + * @param reader the reader to read the json from + * @param map the map to load into + */ + private static void loadJson(BufferedReader reader, Map map) { + SkyblockerMod.GSON.fromJson(reader, JsonObject.class).asMap().forEach((room, jsonElement) -> map.put(room.toLowerCase().replaceAll(" ", "-"), jsonElement)); + } + + private static ArgumentBuilder> markSecretsCommand(boolean found) { + return argument("secret", IntegerArgumentType.integer()).executes(context -> { + int secretIndex = IntegerArgumentType.getInteger(context, "secret"); + if (markSecrets(secretIndex, found)) { + context.getSource().sendFeedback(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFound" : "skyblocker.dungeons.secrets.markSecretMissing", secretIndex)); + } else { + context.getSource().sendError(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFoundUnable" : "skyblocker.dungeons.secrets.markSecretMissingUnable", secretIndex)); + } + return Command.SINGLE_SUCCESS; + }); + } + + /** + * Updates the dungeon. The general idea is similar to the Dungeon Rooms Mod. + *

+ * When entering a new dungeon, this method: + *
    + *
  • Gets the physical northwest corner position of the entrance room and saves it in {@link #physicalEntrancePos}.
  • + *
  • Do nothing until the dungeon map exists.
  • + *
  • Gets the upper left corner of entrance room on the map and saves it in {@link #mapEntrancePos}.
  • + *
  • Gets the size of a room on the map in pixels and saves it in {@link #mapRoomSize}.
  • + *
  • Creates a new {@link Room} with {@link Room.Type} {@link Room.Type.ENTRANCE ENTRANCE} and sets {@link #currentRoom}.
  • + *
+ * When processing an existing dungeon, this method: + *
    + *
  • Calculates the physical northwest corner and upper left corner on the map of the room the player is currently in.
  • + *
  • Gets the room type based on the map color.
  • + *
  • If the room has not been created (when the physical northwest corner is not in {@link #rooms}):
  • + *
      + *
    • If the room type is {@link Room.Type.ROOM}, gets the northwest corner of all connected room segments with {@link DungeonMapUtils#getRoomSegments(MapState, Vector2ic, int, byte)}. (For example, a 1x2 room has two room segments.)
    • + *
    • Create a new room.
    • + *
    + *
  • Sets {@link #currentRoom} to the current room, either created from the previous step or from {@link #rooms}.
  • + *
  • Calls {@link Room#update()} on {@link #currentRoom}.
  • + *
+ */ + @SuppressWarnings("JavadocReference") + private static void update() { + if (!SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints) { + return; + } + if (!Utils.isInDungeons()) { + if (mapEntrancePos != null) { + reset(); + } + return; + } + MinecraftClient client = MinecraftClient.getInstance(); + ClientPlayerEntity player = client.player; + if (player == null || client.world == null) { + return; + } + if (physicalEntrancePos == null) { + Vec3d playerPos = player.getPos(); + physicalEntrancePos = DungeonMapUtils.getPhysicalRoomPos(playerPos); + currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos); + } + ItemStack stack = player.getInventory().main.get(8); + if (!stack.isOf(Items.FILLED_MAP)) { + return; + } + MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world); + if (map == null) { + return; + } + if (mapEntrancePos == null || mapRoomSize == 0) { + ObjectIntPair mapEntrancePosAndSize = DungeonMapUtils.getMapEntrancePosAndRoomSize(map); + if (mapEntrancePosAndSize == null) { + return; + } + mapEntrancePos = mapEntrancePosAndSize.left(); + mapRoomSize = mapEntrancePosAndSize.rightInt(); + LOGGER.info("[Skyblocker] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos); + } + + Vector2ic physicalPos = DungeonMapUtils.getPhysicalRoomPos(client.player.getPos()); + Vector2ic mapPos = DungeonMapUtils.getMapPosFromPhysical(physicalEntrancePos, mapEntrancePos, mapRoomSize, physicalPos); + Room room = rooms.get(physicalPos); + if (room == null) { + Room.Type type = DungeonMapUtils.getRoomType(map, mapPos); + if (type == null || type == Room.Type.UNKNOWN) { + return; + } + switch (type) { + case ENTRANCE, PUZZLE, TRAP, MINIBOSS, FAIRY, BLOOD -> room = newRoom(type, physicalPos); + case ROOM -> room = newRoom(type, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, mapPos, mapRoomSize, type.color))); + } + } + if (room != null && currentRoom != room) { + currentRoom = room; + } + currentRoom.update(); + } + + /** + * Creates a new room with the given type and physical positions, + * adds the room to {@link #rooms}, and sets {@link #currentRoom} to the new room. + * + * @param type the type of room to create + * @param physicalPositions the physical positions of the room + */ + @Nullable + private static Room newRoom(Room.Type type, Vector2ic... physicalPositions) { + try { + Room newRoom = new Room(type, physicalPositions); + for (Vector2ic physicalPos : physicalPositions) { + rooms.put(physicalPos, newRoom); + } + return newRoom; + } catch (IllegalArgumentException e) { + LOGGER.error("[Skyblocker] Failed to create room", e); + } + return null; + } + + /** + * Renders the secret waypoints in {@link #currentRoom} if {@link #isCurrentRoomMatched()}. + */ + private static void render(WorldRenderContext context) { + if (isCurrentRoomMatched()) { + currentRoom.render(context); + } + } + + /** + * Calls {@link Room#onChatMessage(String)} on {@link #currentRoom} if the message is an overlay message and {@link #isCurrentRoomMatched()}. + * Used to detect when all secrets in a room are found. + */ + private static void onChatMessage(Text text, boolean overlay) { + if (overlay && isCurrentRoomMatched()) { + currentRoom.onChatMessage(text.getString()); + } + } + + /** + * Calls {@link Room#onUseBlock(World, BlockHitResult)} on {@link #currentRoom} if {@link #isCurrentRoomMatched()}. + * Used to detect finding {@link SecretWaypoint.Category.CHEST} and {@link SecretWaypoint.Category.WITHER} secrets. + * + * @return {@link ActionResult#PASS} + */ + @SuppressWarnings("JavadocReference") + private static ActionResult onUseBlock(World world, BlockHitResult hitResult) { + if (isCurrentRoomMatched()) { + currentRoom.onUseBlock(world, hitResult); + } + return ActionResult.PASS; + } + + /** + * Calls {@link Room#onItemPickup(ItemEntity, LivingEntity)} on the room the {@code collector} is in if that room {@link #isRoomMatched(Room)}. + * Used to detect finding {@link SecretWaypoint.Category.ITEM} secrets. + * If the collector is the player, {@link #currentRoom} is used as an optimization. + */ + @SuppressWarnings("JavadocReference") + public static void onItemPickup(ItemEntity itemEntity, LivingEntity collector, boolean isPlayer) { + if (isPlayer) { + if (isCurrentRoomMatched()) { + currentRoom.onItemPickup(itemEntity, collector); + } + } else { + Room room = getRoomAtPhysical(collector.getPos()); + if (isRoomMatched(room)) { + room.onItemPickup(itemEntity, collector); + } + } + } + + /** + * Calls {@link Room#onBatRemoved(BatEntity)} on the room the {@code bat} is in if that room {@link #isRoomMatched(Room)}. + * Used to detect finding {@link SecretWaypoint.Category.BAT} secrets. + */ + @SuppressWarnings("JavadocReference") + public static void onBatRemoved(AmbientEntity bat) { + Room room = getRoomAtPhysical(bat.getPos()); + if (isRoomMatched(room)) { + room.onBatRemoved(bat); + } + } + + public static boolean markSecrets(int secretIndex, boolean found) { + if (isCurrentRoomMatched()) { + return currentRoom.markSecrets(secretIndex, found); + } + return false; + } + + /** + * Gets the room at the given physical position. + * + * @param pos the physical position + * @return the room at the given physical position, or null if there is no room at the given physical position + * @see #rooms + * @see DungeonMapUtils#getPhysicalRoomPos(Vec3d) + */ + @Nullable + private static Room getRoomAtPhysical(Vec3d pos) { + return rooms.get(DungeonMapUtils.getPhysicalRoomPos(pos)); + } + + /** + * Calls {@link #isRoomMatched(Room)} on {@link #currentRoom}. + * + * @return {@code true} if {@link #currentRoom} is not null and {@link #isRoomMatched(Room)} + */ + private static boolean isCurrentRoomMatched() { + return isRoomMatched(currentRoom); + } + + /** + * Calls {@link #shouldProcess()} and {@link Room#isMatched()} on the given room. + * + * @param room the room to check + * @return {@code true} if {@link #shouldProcess()}, the given room is not null, and {@link Room#isMatched()} on the given room + */ + @Contract("null -> false") + private static boolean isRoomMatched(@Nullable Room room) { + return shouldProcess() && room != null && room.isMatched(); + } + + /** + * Checks if the player is in a dungeon and {@link SkyblockerConfigManager.Dungeons#secretWaypoints Secret Waypoints} is enabled. + * + * @return whether dungeon secrets should be processed + */ + private static boolean shouldProcess() { + return SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints && Utils.isInDungeons(); + } + + /** + * Resets fields when leaving a dungeon. + */ + private static void reset() { + mapEntrancePos = null; + mapRoomSize = 0; + physicalEntrancePos = null; + rooms.clear(); + currentRoom = null; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java new file mode 100644 index 00000000..dd7dc91e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java @@ -0,0 +1,473 @@ +package de.hysky.skyblocker.skyblock.dungeon.secrets; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.ImmutableTable; +import com.google.common.collect.Table; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.ints.IntRBTreeSet; +import it.unimi.dsi.fastutil.ints.IntSortedSet; +import it.unimi.dsi.fastutil.ints.IntSortedSets; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.util.TriState; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.MapColor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.AmbientEntity; +import net.minecraft.registry.Registries; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; +import org.apache.commons.lang3.tuple.MutableTriple; +import org.apache.commons.lang3.tuple.Triple; +import org.jetbrains.annotations.NotNull; +import org.joml.Vector2i; +import org.joml.Vector2ic; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Room { + private static final Pattern SECRETS = Pattern.compile("§7(\\d{1,2})/(\\d{1,2}) Secrets"); + @NotNull + private final Type type; + @NotNull + private final Set segments; + /** + * The shape of the room. See {@link #getShape(IntSortedSet, IntSortedSet)}. + */ + @NotNull + private final Shape shape; + /** + * The room data containing all rooms for a specific dungeon and {@link #shape}. + */ + private Map roomsData; + /** + * Contains all possible dungeon rooms for this room. The list is gradually shrunk by checking blocks until only one room is left. + */ + private List>> possibleRooms; + /** + * Contains all blocks that have been checked to prevent checking the same block multiple times. + */ + private Set checkedBlocks = new HashSet<>(); + /** + * The task that is used to check blocks. This is used to ensure only one such task can run at a time. + */ + private CompletableFuture findRoom; + private int doubleCheckBlocks; + /** + * Represents the matching state of the room with the following possible values: + *
  • {@link TriState#DEFAULT} means that the room has not been checked, is being processed, or does not {@link Type#needsScanning() need to be processed}. + *
  • {@link TriState#FALSE} means that the room has been checked and there is no match. + *
  • {@link TriState#TRUE} means that the room has been checked and there is a match. + */ + private TriState matched = TriState.DEFAULT; + private Table secretWaypoints; + + public Room(@NotNull Type type, @NotNull Vector2ic... physicalPositions) { + this.type = type; + segments = Set.of(physicalPositions); + IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray())); + IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray())); + shape = getShape(segmentsX, segmentsY); + roomsData = DungeonSecrets.ROOMS_DATA.getOrDefault("catacombs", Collections.emptyMap()).getOrDefault(shape.shape.toLowerCase(), Collections.emptyMap()); + possibleRooms = getPossibleRooms(segmentsX, segmentsY); + } + + @NotNull + public Type getType() { + return type; + } + + public boolean isMatched() { + return matched == TriState.TRUE; + } + + @Override + public String toString() { + return "Room{type=" + type + ", shape=" + shape + ", matched=" + matched + ", segments=" + Arrays.toString(segments.toArray()) + "}"; + } + + @NotNull + private Shape getShape(IntSortedSet segmentsX, IntSortedSet segmentsY) { + return switch (segments.size()) { + case 1 -> Shape.ONE_BY_ONE; + case 2 -> Shape.ONE_BY_TWO; + case 3 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.L_SHAPE : Shape.ONE_BY_THREE; + case 4 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.TWO_BY_TWO : Shape.ONE_BY_FOUR; + default -> throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray())); + }; + } + + private List>> getPossibleRooms(IntSortedSet segmentsX, IntSortedSet segmentsY) { + List possibleDirectionRooms = new ArrayList<>(roomsData.keySet()); + List>> possibleRooms = new ArrayList<>(); + for (Direction direction : getPossibleDirections(segmentsX, segmentsY)) { + possibleRooms.add(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), possibleDirectionRooms)); + } + return possibleRooms; + } + + @NotNull + private Direction[] getPossibleDirections(IntSortedSet segmentsX, IntSortedSet segmentsY) { + return switch (shape) { + case ONE_BY_ONE, TWO_BY_TWO -> Direction.values(); + case ONE_BY_TWO, ONE_BY_THREE, ONE_BY_FOUR -> { + if (segmentsX.size() > 1 && segmentsY.size() == 1) { + yield new Direction[]{Direction.NW, Direction.SE}; + } else if (segmentsX.size() == 1 && segmentsY.size() > 1) { + yield new Direction[]{Direction.NE, Direction.SW}; + } + throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray())); + } + case L_SHAPE -> { + if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()))) { + yield new Direction[]{Direction.SW}; + } else if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.lastInt()))) { + yield new Direction[]{Direction.SE}; + } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.firstInt()))) { + yield new Direction[]{Direction.NW}; + } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.lastInt()))) { + yield new Direction[]{Direction.NE}; + } + throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray())); + } + }; + } + + /** + * Updates the room. + *

    + * This method returns immediately if any of the following conditions are met: + *
      + *
    • The room does not need to be scanned and matched. (When the room is not of type {@link Type.ROOM}, {@link Type.PUZZLE}, or {@link Type.TRAP}. See {@link Type#needsScanning()})
    • + *
    • The room has been matched or failed to match and is on cooldown. See {@link #matched}.
    • + *
    • {@link #findRoom The previous update} has not completed.
    • + *
    + * Then this method tries to match this room through: + *
      + *
    • Iterate over a 11 by 11 by 11 box around the player.
    • + *
    • Check it the block is part of this room and not part of a doorway. See {@link #segments} and {@link #notInDoorway(BlockPos)}.
    • + *
    • Checks if the position has been checked and adds it to {@link #checkedBlocks}.
    • + *
    • Calls {@link #checkBlock(ClientWorld, BlockPos)}
    • + *
    + */ + @SuppressWarnings("JavadocReference") + protected void update() { + // Logical AND has higher precedence than logical OR + if (!type.needsScanning() || matched != TriState.DEFAULT || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) { + return; + } + MinecraftClient client = MinecraftClient.getInstance(); + ClientPlayerEntity player = client.player; + ClientWorld world = client.world; + if (player == null || world == null) { + return; + } + findRoom = CompletableFuture.runAsync(() -> { + for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) { + if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(world, pos)) { + break; + } + } + }); + } + + private static boolean notInDoorway(BlockPos pos) { + if (pos.getY() < 66 || pos.getY() > 73) { + return true; + } + int x = MathHelper.floorMod(pos.getX() - 8, 32); + int z = MathHelper.floorMod(pos.getZ() - 8, 32); + return (x < 13 || x > 17 || z > 2 && z < 28) && (z < 13 || z > 17 || x > 2 && x < 28); + } + + /** + * Filters out dungeon rooms which does not contain the block at the given position. + *

    + * This method: + *
      + *
    • Checks if the block type is included in the dungeon rooms data. See {@link DungeonSecrets#NUMERIC_ID}.
    • + *
    • For each possible direction:
    • + *
        + *
      • Rotate and convert the position to a relative position. See {@link DungeonMapUtils#actualToRelative(Direction, Vector2ic, BlockPos)}.
      • + *
      • Encode the block based on the relative position and the custom numeric block id. See {@link #posIdToInt(BlockPos, byte)}.
      • + *
      • For each possible room in the current direction:
      • + *
          + *
        • Check if {@link #roomsData} contains the encoded block.
        • + *
        • If so, add the room to the new list of possible rooms for this direction.
        • + *
        + *
      • Replace the old possible room list for the current direction with the new one.
      • + *
      + *
    • If there are no matching rooms left:
    • + *
        + *
      • Terminate matching by setting {@link #matched} to {@link TriState#FALSE}.
      • + *
      • Schedule another matching attempt in 50 ticks (2.5 seconds).
      • + *
      • Reset {@link #possibleRooms} and {@link #checkedBlocks} with {@link #reset()}.
      • + *
      • Return {@code true}
      • + *
      + *
    • If there are exactly one room matching:
    • + *
        + *
      • Call {@link #roomMatched(String, Direction, Vector2ic)}.
      • + *
      • Discard the no longer needed fields to save memory.
      • + *
      • Return {@code true}
      • + *
      + *
    • Return {@code false}
    • + *
    + * + * @param world the world to get the block from + * @param pos the position of the block to check + * @return whether room matching should end. Either a match is found or there are no valid rooms left + */ + private boolean checkBlock(ClientWorld world, BlockPos pos) { + byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString()); + if (id == 0) { + return false; + } + for (MutableTriple> directionRooms : possibleRooms) { + int block = posIdToInt(DungeonMapUtils.actualToRelative(directionRooms.getLeft(), directionRooms.getMiddle(), pos), id); + List possibleDirectionRooms = new ArrayList<>(); + for (String room : directionRooms.getRight()) { + if (Arrays.binarySearch(roomsData.get(room), block) >= 0) { + possibleDirectionRooms.add(room); + } + } + directionRooms.setRight(possibleDirectionRooms); + } + + int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum(); + if (matchingRoomsSize == 0) { + // If no rooms match, reset the fields and scan again after 50 ticks. + matched = TriState.FALSE; + DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size()); + Scheduler.INSTANCE.schedule(() -> matched = TriState.DEFAULT, 50); + reset(); + return true; + } else if (matchingRoomsSize == 1 && ++doubleCheckBlocks >= 10) { + // If one room matches, load the secrets for that room and discard the no longer needed fields. + for (Triple> directionRooms : possibleRooms) { + if (directionRooms.getRight().size() == 1) { + roomMatched(directionRooms.getRight().get(0), directionRooms.getLeft(), directionRooms.getMiddle()); + discard(); + return true; + } + } + return false; // This should never happen, we just checked that there is one possible room, and the return true in the loop should activate + } else { + DungeonSecrets.LOGGER.debug("[Skyblocker] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size()); + return false; + } + } + + /** + * Encodes a {@link BlockPos} and the custom numeric block id into an integer. + * + * @param pos the position of the block + * @param id the custom numeric block id + * @return the encoded integer + */ + private int posIdToInt(BlockPos pos, byte id) { + return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id; + } + + /** + * Loads the secret waypoints for the room from {@link DungeonSecrets#waypointsJson} once it has been matched + * and sets {@link #matched} to {@link TriState#TRUE}. + * + * @param directionRooms the direction, position, and name of the room + */ + @SuppressWarnings("JavadocReference") + private void roomMatched(String name, Direction direction, Vector2ic physicalCornerPos) { + Table secretWaypointsMutable = HashBasedTable.create(); + for (JsonElement waypointElement : DungeonSecrets.getRoomWaypoints(name)) { + JsonObject waypoint = waypointElement.getAsJsonObject(); + String secretName = waypoint.get("secretName").getAsString(); + int secretIndex = Integer.parseInt(secretName.substring(0, Character.isDigit(secretName.charAt(1)) ? 2 : 1)); + BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint); + secretWaypointsMutable.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos)); + } + secretWaypoints = ImmutableTable.copyOf(secretWaypointsMutable); + matched = TriState.TRUE; + DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size()); + } + + /** + * Resets fields for another round of matching after room matching fails. + */ + private void reset() { + IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray())); + IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray())); + possibleRooms = getPossibleRooms(segmentsX, segmentsY); + checkedBlocks = new HashSet<>(); + doubleCheckBlocks = 0; + } + + /** + * Discards fields after room matching completes when a room is found. + * These fields are no longer needed and are discarded to save memory. + */ + private void discard() { + roomsData = null; + possibleRooms = null; + checkedBlocks = null; + doubleCheckBlocks = 0; + } + + /** + * Calls {@link SecretWaypoint#render(WorldRenderContext)} on {@link #secretWaypoints all secret waypoints}. + */ + protected void render(WorldRenderContext context) { + for (SecretWaypoint secretWaypoint : secretWaypoints.values()) { + if (secretWaypoint.shouldRender()) { + secretWaypoint.render(context); + } + } + } + + /** + * Sets all secrets as found if {@link #isAllSecretsFound(String)}. + */ + protected void onChatMessage(String message) { + if (isAllSecretsFound(message)) { + secretWaypoints.values().forEach(SecretWaypoint::setFound); + } + } + + /** + * Checks if the number of found secrets is equals or greater than the total number of secrets in the room. + * + * @param message the message to check in + * @return whether the number of found secrets is equals or greater than the total number of secrets in the room + */ + protected static boolean isAllSecretsFound(String message) { + Matcher matcher = SECRETS.matcher(message); + if (matcher.find()) { + return Integer.parseInt(matcher.group(1)) >= Integer.parseInt(matcher.group(2)); + } + return false; + } + + /** + * Marks the secret at the interaction position as found when the player interacts with a chest or a player head, + * if there is a secret at the interaction position. + * + * @param world the world to get the block from + * @param hitResult the block being interacted with + * @see #onSecretFound(SecretWaypoint, String, Object...) + */ + protected void onUseBlock(World world, BlockHitResult hitResult) { + BlockState state = world.getBlockState(hitResult.getBlockPos()); + if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) { + secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::needsInteraction).findAny() + .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex)); + } else if (state.isOf(Blocks.LEVER)) { + secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound); + } + } + + /** + * Marks the closest secret that requires item pickup no greater than 6 blocks away as found when the player picks up a secret item. + * + * @param itemEntity the item entity being picked up + * @param collector the collector of the item + * @see #onSecretFound(SecretWaypoint, String, Object...) + */ + protected void onItemPickup(ItemEntity itemEntity, LivingEntity collector) { + if (SecretWaypoint.SECRET_ITEMS.stream().noneMatch(itemEntity.getStack().getName().getString()::contains)) { + return; + } + secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(collector))).filter(SecretWaypoint.getRangePredicate(collector)) + .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); + } + + /** + * Marks the closest bat secret as found when a bat is killed. + * + * @param bat the bat being killed + * @see #onSecretFound(SecretWaypoint, String, Object...) + */ + protected void onBatRemoved(AmbientEntity bat) { + secretWaypoints.values().stream().filter(SecretWaypoint::isBat).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(bat))) + .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); + } + + /** + * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found. + * + * @param secretWaypoint the secret waypoint to read the index from. + * @param msg the message to log + * @param args the args for the {@link org.slf4j.Logger#info(String, Object...) Logger#info(String, Object...)} call + */ + private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) { + secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound); + DungeonSecrets.LOGGER.info(msg, args); + } + + protected boolean markSecrets(int secretIndex, boolean found) { + Map secret = secretWaypoints.row(secretIndex); + if (secret.isEmpty()) { + return false; + } else { + secret.values().forEach(found ? SecretWaypoint::setFound : SecretWaypoint::setMissing); + return true; + } + } + + public enum Type { + ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)), + ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)), + PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)), + TRAP(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.HIGH)), + MINIBOSS(MapColor.YELLOW.getRenderColorByte(MapColor.Brightness.HIGH)), + FAIRY(MapColor.PINK.getRenderColorByte(MapColor.Brightness.HIGH)), + BLOOD(MapColor.BRIGHT_RED.getRenderColorByte(MapColor.Brightness.HIGH)), + UNKNOWN(MapColor.GRAY.getRenderColorByte(MapColor.Brightness.NORMAL)); + final byte color; + + Type(byte color) { + this.color = color; + } + + /** + * @return whether this room type has secrets and needs to be scanned and matched. + */ + private boolean needsScanning() { + return switch (this) { + case ROOM, PUZZLE, TRAP -> true; + default -> false; + }; + } + } + + private enum Shape { + ONE_BY_ONE("1x1"), + ONE_BY_TWO("1x2"), + ONE_BY_THREE("1x3"), + ONE_BY_FOUR("1x4"), + L_SHAPE("L-shape"), + TWO_BY_TWO("2x2"); + final String shape; + + Shape(String shape) { + this.shape = shape; + } + + @Override + public String toString() { + return shape; + } + } + + public enum Direction { + NW, NE, SW, SE + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java new file mode 100644 index 00000000..d2a31ea3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java @@ -0,0 +1,142 @@ +package de.hysky.skyblocker.skyblock.dungeon.secrets; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +import java.util.List; +import java.util.function.Predicate; +import java.util.function.ToDoubleFunction; + +public class SecretWaypoint { + static final List SECRET_ITEMS = List.of("Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "Treasure Talisman"); + final int secretIndex; + final Category category; + private final Text name; + private final BlockPos pos; + private final Vec3d centerPos; + private boolean missing; + + SecretWaypoint(int secretIndex, JsonObject waypoint, String name, BlockPos pos) { + this.secretIndex = secretIndex; + this.category = Category.get(waypoint); + this.name = Text.of(name); + this.pos = pos; + this.centerPos = pos.toCenterPos(); + this.missing = true; + } + + static ToDoubleFunction getSquaredDistanceToFunction(Entity entity) { + return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos); + } + + static Predicate getRangePredicate(Entity entity) { + return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos) <= 36D; + } + + boolean shouldRender() { + return category.isEnabled() && missing; + } + + boolean needsInteraction() { + return category.needsInteraction(); + } + + boolean isLever() { + return category.isLever(); + } + + boolean needsItemPickup() { + return category.needsItemPickup(); + } + + boolean isBat() { + return category.isBat(); + } + + void setFound() { + this.missing = false; + } + + void setMissing() { + this.missing = true; + } + + /** + * Renders the secret waypoint, including a filled cube, a beacon beam, the name, and the distance from the player. + */ + void render(WorldRenderContext context) { + RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, pos, category.colorComponents, 0.5F); + Vec3d posUp = centerPos.add(0, 1, 0); + RenderHelper.renderText(context, name, posUp, true); + double distance = context.camera().getPos().distanceTo(centerPos); + RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true); + } + + enum Category { + ENTRANCE(secretWaypoints -> secretWaypoints.enableEntranceWaypoints, 0, 255, 0), + SUPERBOOM(secretWaypoints -> secretWaypoints.enableSuperboomWaypoints, 255, 0, 0), + CHEST(secretWaypoints -> secretWaypoints.enableChestWaypoints, 2, 213, 250), + ITEM(secretWaypoints -> secretWaypoints.enableItemWaypoints, 2, 64, 250), + BAT(secretWaypoints -> secretWaypoints.enableBatWaypoints, 142, 66, 0), + WITHER(secretWaypoints -> secretWaypoints.enableWitherWaypoints, 30, 30, 30), + LEVER(secretWaypoints -> secretWaypoints.enableLeverWaypoints, 250, 217, 2), + FAIRYSOUL(secretWaypoints -> secretWaypoints.enableFairySoulWaypoints, 255, 85, 255), + STONK(secretWaypoints -> secretWaypoints.enableStonkWaypoints, 146, 52, 235), + DEFAULT(secretWaypoints -> secretWaypoints.enableDefaultWaypoints, 190, 255, 252); + private final Predicate enabledPredicate; + private final float[] colorComponents; + + Category(Predicate enabledPredicate, int... intColorComponents) { + this.enabledPredicate = enabledPredicate; + colorComponents = new float[intColorComponents.length]; + for (int i = 0; i < intColorComponents.length; i++) { + colorComponents[i] = intColorComponents[i] / 255F; + } + } + + private static Category get(JsonObject categoryJson) { + return switch (categoryJson.get("category").getAsString()) { + case "entrance" -> Category.ENTRANCE; + case "superboom" -> Category.SUPERBOOM; + case "chest" -> Category.CHEST; + case "item" -> Category.ITEM; + case "bat" -> Category.BAT; + case "wither" -> Category.WITHER; + case "lever" -> Category.LEVER; + case "fairysoul" -> Category.FAIRYSOUL; + case "stonk" -> Category.STONK; + default -> Category.DEFAULT; + }; + } + + boolean needsInteraction() { + return this == CHEST || this == WITHER; + } + + boolean isLever() { + return this == LEVER; + } + + boolean needsItemPickup() { + return this == ITEM; + } + + boolean isBat() { + return this == BAT; + } + + boolean isEnabled() { + return enabledPredicate.test(SkyblockerConfigManager.get().locations.dungeons.secretWaypoints); + } + } +} 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 new file mode 100644 index 00000000..6e9eb02d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java @@ -0,0 +1,72 @@ +package de.hysky.skyblocker.skyblock.dungeon.terminal; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.registry.Registries; +import net.minecraft.util.DyeColor; +import net.minecraft.util.Identifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + + +public class ColorTerminal extends ContainerSolver { + private static final Logger LOGGER = LoggerFactory.getLogger(ColorTerminal.class.getName()); + private static final Map colorFromName; + private DyeColor targetColor; + private static final Map itemColor; + + public ColorTerminal() { + super("^Select all the ([A-Z ]+) items!$"); + } + + @Override + protected boolean isEnabled() { + targetColor = null; + return SkyblockerConfigManager.get().locations.dungeons.terminals.solveColor; + } + + @Override + protected List getColors(String[] groups, Map slots) { + trimEdges(slots, 6); + List highlights = new ArrayList<>(); + String colorString = groups[0]; + if (targetColor == null) { + targetColor = colorFromName.get(colorString); + if (targetColor == null) { + LOGGER.error("[Skyblocker] Couldn't find dye color corresponding to \"" + colorString + "\""); + return Collections.emptyList(); + } + } + for (Map.Entry slot : slots.entrySet()) { + ItemStack itemStack = slot.getValue(); + if (!itemStack.hasEnchantments() && targetColor.equals(itemColor.get(itemStack.getItem()))) { + highlights.add(ColorHighlight.green(slot.getKey())); + } + } + return highlights; + } + + + static { + colorFromName = new HashMap<>(); + for (DyeColor color : DyeColor.values()) + colorFromName.put(color.getName().toUpperCase(Locale.ENGLISH), color); + colorFromName.put("SILVER", DyeColor.LIGHT_GRAY); + colorFromName.put("LIGHT BLUE", DyeColor.LIGHT_BLUE); + + itemColor = new HashMap<>(); + for (DyeColor color : DyeColor.values()) + for (String item : new String[]{"dye", "wool", "stained_glass", "terracotta"}) + itemColor.put(Registries.ITEM.get(new Identifier(color.getName() + '_' + item)), color); + itemColor.put(Items.BONE_MEAL, DyeColor.WHITE); + itemColor.put(Items.LAPIS_LAZULI, DyeColor.BLUE); + itemColor.put(Items.COCOA_BEANS, DyeColor.BROWN); + itemColor.put(Items.INK_SAC, DyeColor.BLACK); + } +} \ No newline at end of file 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 new file mode 100644 index 00000000..b2636373 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java @@ -0,0 +1,58 @@ +package de.hysky.skyblocker.skyblock.dungeon.terminal; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class OrderTerminal extends ContainerSolver { + private final int PANES_NUM = 14; + private int[] orderedSlots; + private int currentNum = Integer.MAX_VALUE; + + public OrderTerminal() { + super("^Click in order!$"); + } + + @Override + protected boolean isEnabled() { + orderedSlots = null; + currentNum = 0; + return SkyblockerConfigManager.get().locations.dungeons.terminals.solveOrder; + } + + @Override + protected List getColors(String[] groups, Map slots) { + if(orderedSlots == null && !orderSlots(slots)) + return Collections.emptyList(); + while(currentNum < PANES_NUM && Items.LIME_STAINED_GLASS_PANE.equals(slots.get(orderedSlots[currentNum]).getItem())) + currentNum++; + List highlights = new ArrayList<>(3); + int last = Integer.min(3, PANES_NUM - currentNum); + for(int i = 0; i < last; i++) { + highlights.add(new ColorHighlight(orderedSlots[currentNum + i], (224 - 64 * i) << 24 | 64 << 16 | 96 << 8 | 255)); + } + return highlights; + } + + public boolean orderSlots(Map slots) { + trimEdges(slots, 4); + orderedSlots = new int[PANES_NUM]; + for(Map.Entry slot : slots.entrySet()) { + if(Items.AIR.equals(slot.getValue().getItem())) { + orderedSlots = null; + return false; + } + else + orderedSlots[slot.getValue().getCount() - 1] = slot.getKey(); + } + currentNum = 0; + return true; + } +} \ 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 new file mode 100644 index 00000000..5f856af2 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.dungeon.terminal; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class StartsWithTerminal extends ContainerSolver { + public StartsWithTerminal() { + super("^What starts with: '([A-Z])'\\?$"); + } + + @Override + protected boolean isEnabled() { + return SkyblockerConfigManager.get().locations.dungeons.terminals.solveStartsWith; + } + + @Override + protected List getColors(String[] groups, Map slots) { + trimEdges(slots, 6); + String prefix = groups[0]; + List highlights = new ArrayList<>(); + for (Map.Entry slot : slots.entrySet()) { + ItemStack stack = slot.getValue(); + if (!stack.hasEnchantments() && stack.getName().getString().startsWith(prefix)) { + highlights.add(ColorHighlight.green(slot.getKey())); + } + } + return highlights; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java new file mode 100644 index 00000000..b853d7cc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java @@ -0,0 +1,144 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class DwarvenHud { + + public static final MinecraftClient client = MinecraftClient.getInstance(); + public static List commissionList = new ArrayList<>(); + + public static final List COMMISSIONS = Stream.of( + "(?:Titanium|Mithril|Hard Stone) Miner", + "(?:Ice Walker|Goblin|Goblin Raid|Automaton|Sludge|Team Treasurite Member|Yog|Boss Corleone|Thyst) Slayer", + "(?:Lava Springs|Cliffside Veins|Rampart's Quarry|Upper Mines|Royal Mines) Mithril", + "(?:Lava Springs|Cliffside Veins|Rampart's Quarry|Upper Mines|Royal Mines) Titanium", + "Goblin Raid", + "(?:Powder Ghast|Star Sentry) Puncher", + "(? Pattern.compile("^.*(" + s + "): (\\d+\\.?\\d*%|DONE)")) + .collect(Collectors.toList()); + + public static void init() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("hud") + .then(ClientCommandManager.literal("dwarven") + .executes(Scheduler.queueOpenScreenCommand(DwarvenHudConfigScreen::new)))))); + + HudRenderCallback.EVENT.register((context, tickDelta) -> { + if (!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabled + || client.options.playerListKey.isPressed() + || client.player == null + || commissionList.isEmpty()) { + return; + } + render(HudCommsWidget.INSTANCE, context, SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x, + SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y, commissionList); + }); + } + + public static IntIntPair getDimForConfig(List commissions) { + return switch (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.style) { + case SIMPLE -> { + HudCommsWidget.INSTANCE_CFG.updateData(commissions, false); + yield IntIntPair.of( + HudCommsWidget.INSTANCE_CFG.getWidth(), + HudCommsWidget.INSTANCE_CFG.getHeight()); + } + case FANCY -> { + HudCommsWidget.INSTANCE_CFG.updateData(commissions, true); + yield IntIntPair.of( + HudCommsWidget.INSTANCE_CFG.getWidth(), + HudCommsWidget.INSTANCE_CFG.getHeight()); + } + default -> IntIntPair.of(200, 20 * commissions.size()); + }; + } + + public static void render(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List commissions) { + + switch (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.style) { + case SIMPLE -> renderSimple(hcw, context, hudX, hudY, commissions); + case FANCY -> renderFancy(hcw, context, hudX, hudY, commissions); + case CLASSIC -> renderClassic(context, hudX, hudY, commissions); + } + } + + public static void renderClassic(DrawContext context, int hudX, int hudY, List commissions) { + if (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enableBackground) { + context.fill(hudX, hudY, hudX + 200, hudY + (20 * commissions.size()), 0x64000000); + } + + int y = 0; + for (Commission commission : commissions) { + context + .drawTextWithShadow(client.textRenderer, + Text.literal(commission.commission + ": ") + .styled(style -> style.withColor(Formatting.AQUA)) + .append(Text.literal(commission.progression) + .styled(style -> style.withColor(Formatting.GREEN))), + hudX + 5, hudY + y + 5, 0xFFFFFFFF); + y += 20; + } + } + + public static void renderSimple(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List commissions) { + hcw.updateData(commissions, false); + hcw.update(); + hcw.setX(hudX); + hcw.setY(hudY); + hcw.render(context, + SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enableBackground); + } + + public static void renderFancy(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List commissions) { + hcw.updateData(commissions, true); + hcw.update(); + hcw.setX(hudX); + hcw.setY(hudY); + hcw.render(context, + SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enableBackground); + } + + public static void update() { + commissionList = new ArrayList<>(); + if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabled) + return; + + client.getNetworkHandler().getPlayerList().forEach(playerListEntry -> { + if (playerListEntry.getDisplayName() != null) { + for (Pattern pattern : COMMISSIONS) { + Matcher matcher = pattern.matcher(playerListEntry.getDisplayName().getString()); + if (matcher.find()) { + commissionList.add(new Commission(matcher.group(1), matcher.group(2))); + } + + } + } + }); + } + + // steamroller tactics to get visibility from outside classes (HudCommsWidget) + public record Commission(String commission, String progression) { + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java new file mode 100644 index 00000000..7b62221e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java @@ -0,0 +1,66 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget; +import de.hysky.skyblocker.utils.render.RenderHelper; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.text.Text; + +import java.awt.*; +import java.util.List; + +public class DwarvenHudConfigScreen extends Screen { + + private static final List CFG_COMMS = List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")); + + private int hudX = SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x; + private int hudY = SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y; + private final Screen parent; + + protected DwarvenHudConfigScreen() { + this(null); + } + + public DwarvenHudConfigScreen(Screen parent) { + super(Text.of("Dwarven HUD Config")); + this.parent = parent; + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + renderBackground(context, mouseX, mouseY, delta); + DwarvenHud.render(HudCommsWidget.INSTANCE_CFG, context, hudX, hudY, List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%"))); + context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB()); + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + IntIntPair dims = DwarvenHud.getDimForConfig(CFG_COMMS); + if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + 200, hudY + 40) && button == 0) { + hudX = (int) Math.max(Math.min(mouseX - (double) dims.leftInt() / 2, this.width - dims.leftInt()), 0); + hudY = (int) Math.max(Math.min(mouseY - (double) dims.rightInt() / 2, this.height - dims.rightInt()), 0); + } + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == 1) { + IntIntPair dims = DwarvenHud.getDimForConfig(CFG_COMMS); + hudX = this.width / 2 - dims.leftInt(); + hudY = this.height / 2 - dims.rightInt(); + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public void close() { + SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x = hudX; + SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y = hudY; + SkyblockerConfigManager.save(); + client.setScreen(parent); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/Fetchur.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/Fetchur.java new file mode 100644 index 00000000..9bfb77f7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/Fetchur.java @@ -0,0 +1,53 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; + +public class Fetchur extends ChatPatternListener { + private static final Map answers; + + public Fetchur() { + super("^§e\\[NPC] Fetchur§f: (?:its|theyre) ([a-zA-Z, \\-]*)$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().locations.dwarvenMines.solveFetchur ? ChatFilterResult.FILTER : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null) return false; + String riddle = matcher.group(1); + String answer = answers.getOrDefault(riddle, riddle); + client.player.sendMessage(Text.of("§e[NPC] Fetchur§f: " + answer), false); + return true; + } + + static { + answers = new HashMap<>(); + answers.put("red and soft", Text.translatable("block.minecraft.red_wool").getString()); + answers.put("yellow and see through", Text.translatable("block.minecraft.yellow_stained_glass").getString()); + answers.put("circular and sometimes moves", Text.translatable("item.minecraft.compass").getString()); + // TODO remove when typo fixed by hypixel + answers.put("circlular and sometimes moves", Text.translatable("item.minecraft.compass").getString()); + answers.put("expensive minerals", "Mithril"); + answers.put("useful during celebrations", Text.translatable("item.minecraft.firework_rocket").getString()); + answers.put("hot and gives energy", "Cheap / Decent Coffee"); + answers.put("tall and can be opened", Text.translatable("block.minecraft.oak_door").getString()); + answers.put("brown and fluffy", Text.translatable("item.minecraft.rabbit_foot").getString()); + answers.put("explosive but more than usual", "Superboom TNT"); + answers.put("wearable and grows", Text.translatable("block.minecraft.pumpkin").getString()); + answers.put("shiny and makes sparks", Text.translatable("item.minecraft.flint_and_steel").getString()); + answers.put("red and white and you can mine it", Text.translatable("block.minecraft.nether_quartz_ore").getString()); + answers.put("round and green, or purple", Text.translatable("item.minecraft.ender_pearl").getString()); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/Puzzler.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/Puzzler.java new file mode 100644 index 00000000..fae845b5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/Puzzler.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; + +import java.util.regex.Matcher; + +public class Puzzler extends ChatPatternListener { + public Puzzler() { + super("^§e\\[NPC] §dPuzzler§f: ((?:§d▲|§5▶|§b◀|§a▼){10})$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().locations.dwarvenMines.solvePuzzler ? null : ChatFilterResult.PASS; + } + + @Override + public boolean onMatch(Text message, Matcher matcher) { + int x = 181; + int z = 135; + for (char c : matcher.group(1).toCharArray()) { + if (c == '▲') z++; + else if (c == '▼') z--; + else if (c == '◀') x++; + else if (c == '▶') x--; + } + ClientWorld world = MinecraftClient.getInstance().world; + if (world != null) + world.setBlockState(new BlockPos(x, 195, z), Blocks.CRIMSON_PLANKS.getDefaultState()); + return false; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java new file mode 100644 index 00000000..19459b43 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ChronomatronSolver.java @@ -0,0 +1,129 @@ +package de.hysky.skyblocker.skyblock.experiment; + +import com.google.common.collect.ImmutableMap; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class ChronomatronSolver extends ExperimentSolver { + public static final ImmutableMap TERRACOTTA_TO_GLASS = ImmutableMap.ofEntries( + new AbstractMap.SimpleImmutableEntry<>(Items.RED_TERRACOTTA, Items.RED_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.ORANGE_TERRACOTTA, Items.ORANGE_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.YELLOW_TERRACOTTA, Items.YELLOW_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.LIME_TERRACOTTA, Items.LIME_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.GREEN_TERRACOTTA, Items.GREEN_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.CYAN_TERRACOTTA, Items.CYAN_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.LIGHT_BLUE_TERRACOTTA, Items.LIGHT_BLUE_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.BLUE_TERRACOTTA, Items.BLUE_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.PURPLE_TERRACOTTA, Items.PURPLE_STAINED_GLASS), + new AbstractMap.SimpleImmutableEntry<>(Items.PINK_TERRACOTTA, Items.PINK_STAINED_GLASS) + ); + + private final List chronomatronSlots = new ArrayList<>(); + private int chronomatronChainLengthCount; + private int chronomatronCurrentSlot; + private int chronomatronCurrentOrdinal; + + public ChronomatronSolver() { + super("^Chronomatron \\(\\w+\\)$"); + } + + public List getChronomatronSlots() { + return chronomatronSlots; + } + + public int getChronomatronCurrentOrdinal() { + return chronomatronCurrentOrdinal; + } + + public int incrementChronomatronCurrentOrdinal() { + return ++chronomatronCurrentOrdinal; + } + + @Override + protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) { + return experimentsConfig.enableChronomatronSolver; + } + + @Override + protected void tick(Screen screen) { + if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Chronomatron (")) { + switch (getState()) { + case REMEMBER -> { + Inventory inventory = genericContainerScreen.getScreenHandler().getInventory(); + if (chronomatronCurrentSlot == 0) { + for (int index = 10; index < 43; index++) { + if (inventory.getStack(index).hasEnchantments()) { + if (chronomatronSlots.size() <= chronomatronChainLengthCount) { + chronomatronSlots.add(TERRACOTTA_TO_GLASS.get(inventory.getStack(index).getItem())); + setState(State.WAIT); + } else { + chronomatronChainLengthCount++; + } + chronomatronCurrentSlot = index; + return; + } + } + } else if (!inventory.getStack(chronomatronCurrentSlot).hasEnchantments()) { + chronomatronCurrentSlot = 0; + } + } + case WAIT -> { + if (genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString().startsWith("Timer: ")) { + setState(State.SHOW); + } + } + case END -> { + String name = genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString(); + if (!name.startsWith("Timer: ")) { + if (name.equals("Remember the pattern!")) { + chronomatronChainLengthCount = 0; + chronomatronCurrentOrdinal = 0; + setState(State.REMEMBER); + } else { + reset(); + } + } + } + } + } else { + reset(); + } + } + + @Override + protected List getColors(String[] groups, Map slots) { + List highlights = new ArrayList<>(); + if (getState() == State.SHOW && chronomatronSlots.size() > chronomatronCurrentOrdinal) { + for (Map.Entry indexStack : slots.entrySet()) { + int index = indexStack.getKey(); + ItemStack stack = indexStack.getValue(); + Item item = chronomatronSlots.get(chronomatronCurrentOrdinal); + if (stack.isOf(item) || TERRACOTTA_TO_GLASS.get(stack.getItem()) == item) { + highlights.add(ColorHighlight.green(index)); + } + } + } + return highlights; + } + + @Override + protected void reset() { + super.reset(); + chronomatronSlots.clear(); + chronomatronChainLengthCount = 0; + chronomatronCurrentSlot = 0; + chronomatronCurrentOrdinal = 0; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java new file mode 100644 index 00000000..6efcd420 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/ExperimentSolver.java @@ -0,0 +1,60 @@ +package de.hysky.skyblocker.skyblock.experiment; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.item.ItemStack; + +import java.util.HashMap; +import java.util.Map; + +public abstract class ExperimentSolver extends ContainerSolver { + public enum State { + REMEMBER, WAIT, SHOW, END + } + + private State state = State.REMEMBER; + private final Map slots = new HashMap<>(); + + protected ExperimentSolver(String containerName) { + super(containerName); + } + + public State getState() { + return state; + } + + public void setState(State state) { + this.state = state; + } + + public Map getSlots() { + return slots; + } + + @Override + protected final boolean isEnabled() { + return isEnabled(SkyblockerConfigManager.get().general.experiments); + } + + protected abstract boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig); + + @Override + protected void start(GenericContainerScreen screen) { + super.start(screen); + state = State.REMEMBER; + ScreenEvents.afterTick(screen).register(this::tick); + } + + @Override + protected void reset() { + super.reset(); + state = State.REMEMBER; + slots.clear(); + } + + protected abstract void tick(Screen screen); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java new file mode 100644 index 00000000..c00249fe --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/SuperpairsSolver.java @@ -0,0 +1,81 @@ +package de.hysky.skyblocker.skyblock.experiment; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +import java.util.*; + +public class SuperpairsSolver extends ExperimentSolver { + private int superpairsPrevClickedSlot; + private ItemStack superpairsCurrentSlot; + private final Set superpairsDuplicatedSlots = new HashSet<>(); + + public SuperpairsSolver() { + super("^Superpairs \\(\\w+\\)$"); + } + + public void setSuperpairsPrevClickedSlot(int superpairsPrevClickedSlot) { + this.superpairsPrevClickedSlot = superpairsPrevClickedSlot; + } + + public void setSuperpairsCurrentSlot(ItemStack superpairsCurrentSlot) { + this.superpairsCurrentSlot = superpairsCurrentSlot; + } + + @Override + protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) { + return experimentsConfig.enableSuperpairsSolver; + } + + @Override + protected void start(GenericContainerScreen screen) { + super.start(screen); + setState(State.SHOW); + } + + @Override + protected void tick(Screen screen) { + if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Superpairs (")) { + if (getState() == State.SHOW) { + if (genericContainerScreen.getScreenHandler().getInventory().getStack(4).isOf(Items.CAULDRON)) { + reset(); + } else if (getSlots().get(superpairsPrevClickedSlot) == null) { + ItemStack itemStack = genericContainerScreen.getScreenHandler().getInventory().getStack(superpairsPrevClickedSlot); + if (!(itemStack.isOf(Items.CYAN_STAINED_GLASS) || itemStack.isOf(Items.BLACK_STAINED_GLASS_PANE) || itemStack.isOf(Items.AIR))) { + getSlots().entrySet().stream().filter((entry -> ItemStack.areEqual(entry.getValue(), itemStack))).findAny().ifPresent(entry -> superpairsDuplicatedSlots.add(entry.getKey())); + getSlots().put(superpairsPrevClickedSlot, itemStack); + superpairsCurrentSlot = itemStack; + } + } + } + } else { + reset(); + } + } + + @Override + protected List getColors(String[] groups, Map displaySlots) { + List highlights = new ArrayList<>(); + if (getState() == State.SHOW) { + for (Map.Entry indexStack : displaySlots.entrySet()) { + int index = indexStack.getKey(); + ItemStack displayStack = indexStack.getValue(); + ItemStack stack = getSlots().get(index); + if (stack != null && !ItemStack.areEqual(stack, displayStack)) { + if (ItemStack.areEqual(superpairsCurrentSlot, stack) && displayStack.getName().getString().equals("Click a second button!")) { + highlights.add(ColorHighlight.green(index)); + } else if (superpairsDuplicatedSlots.contains(index)) { + highlights.add(ColorHighlight.yellow(index)); + } else { + highlights.add(ColorHighlight.red(index)); + } + } + } + } + return highlights; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java b/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java new file mode 100644 index 00000000..1fcb976b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/experiment/UltrasequencerSolver.java @@ -0,0 +1,80 @@ +package de.hysky.skyblocker.skyblock.experiment; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class UltrasequencerSolver extends ExperimentSolver { + private int ultrasequencerNextSlot; + + public UltrasequencerSolver() { + super("^Ultrasequencer \\(\\w+\\)$"); + } + + public int getUltrasequencerNextSlot() { + return ultrasequencerNextSlot; + } + + public void setUltrasequencerNextSlot(int ultrasequencerNextSlot) { + this.ultrasequencerNextSlot = ultrasequencerNextSlot; + } + + @Override + protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) { + return experimentsConfig.enableUltrasequencerSolver; + } + + @Override + protected void tick(Screen screen) { + if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Ultrasequencer (")) { + switch (getState()) { + case REMEMBER -> { + Inventory inventory = genericContainerScreen.getScreenHandler().getInventory(); + if (inventory.getStack(49).getName().getString().equals("Remember the pattern!")) { + for (int index = 9; index < 45; index++) { + ItemStack itemStack = inventory.getStack(index); + String name = itemStack.getName().getString(); + if (name.matches("\\d+")) { + if (name.equals("1")) { + ultrasequencerNextSlot = index; + } + getSlots().put(index, itemStack); + } + } + setState(State.WAIT); + } + } + case WAIT -> { + if (genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString().startsWith("Timer: ")) { + setState(State.SHOW); + } + } + case END -> { + String name = genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString(); + if (!name.startsWith("Timer: ")) { + if (name.equals("Remember the pattern!")) { + getSlots().clear(); + setState(State.REMEMBER); + } else { + reset(); + } + } + } + } + } else { + reset(); + } + } + + @Override + protected List getColors(String[] groups, Map slots) { + return getState() == State.SHOW && ultrasequencerNextSlot != 0 ? List.of(ColorHighlight.green(ultrasequencerNextSlot)) : new ArrayList<>(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java new file mode 100644 index 00000000..db10e952 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; + +public class AbilityFilter extends SimpleChatFilter { + public AbilityFilter() { + super("^(?:This ability is on cooldown for " + NUMBER + "s\\.|No more charges, next one in " + NUMBER + "s!)$"); + } + + @Override + protected ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideAbility; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/AdFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/AdFilter.java new file mode 100644 index 00000000..5860b41e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/AdFilter.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.text.Text; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AdFilter extends ChatPatternListener { + private static final Pattern[] AD_FILTERS = new Pattern[] { + Pattern.compile("^(?:i(?:m|'m| am)? |(?:is )?any(?: ?one|1) )?(?:buy|sell|lowball|trade?)(?:ing)?(?:\\W|$)", Pattern.CASE_INSENSITIVE), + Pattern.compile("(.)\\1{7,}"), + Pattern.compile("\\W(?:on|in|check|at) my (?:ah|bin)(?:\\W|$)", Pattern.CASE_INSENSITIVE), }; + + public AdFilter() { + // Groups: + // 1. Player name + // 2. Message + // (?:§8\[[§feadbc0-9]+§8\] )?(?:[§76l]+[] )?§[67abc](?:\[[§A-Za-z0-9+]+\] )?([A-Za-z0-9_]+)§[f7]: (.+) + super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + Constants.LEVEL_EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)§[f7]: (.+)"); + } + + @Override + public boolean onMatch(Text _message, Matcher matcher) { + String message = matcher.group(2); + for (Pattern adFilter : AD_FILTERS) + if (adFilter.matcher(message).find()) + return true; + return false; + } + + @Override + protected ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideAds; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/AoteFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/AoteFilter.java new file mode 100644 index 00000000..5d660037 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/AoteFilter.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; + +public class AoteFilter extends SimpleChatFilter { + public AoteFilter() { + super("^There are blocks in the way!$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideAOTE; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/AutopetFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/AutopetFilter.java new file mode 100644 index 00000000..f97e8177 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/AutopetFilter.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +import java.util.Objects; +import java.util.regex.Matcher; + +public class AutopetFilter extends ChatPatternListener { + public AutopetFilter() { + super("^§cAutopet §eequipped your §7.*§e! §a§lVIEW RULE$"); + } + + @Override + public boolean onMatch(Text _message, Matcher matcher) { + if (SkyblockerConfigManager.get().messages.hideAutopet == ChatFilterResult.ACTION_BAR) { + Objects.requireNonNull(MinecraftClient.getInstance().player).sendMessage( + Text.literal( + _message.getString().replace("§a§lVIEW RULE", "") + ), true); + } + return true; + } + + @Override + public ChatFilterResult state() { + if (SkyblockerConfigManager.get().messages.hideAutopet == ChatFilterResult.ACTION_BAR) + return ChatFilterResult.FILTER; + else + return SkyblockerConfigManager.get().messages.hideAutopet; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/ComboFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/ComboFilter.java new file mode 100644 index 00000000..5fd6f741 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/ComboFilter.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; + +public class ComboFilter extends SimpleChatFilter { + public ComboFilter() { + super("^(\\+\\d+ Kill Combo \\+\\d+(% ✯ Magic Find| coins per kill|% Combat Exp)" + + "|Your Kill Combo has expired! You reached a \\d+ Kill Combo!)$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideCombo; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java new file mode 100644 index 00000000..371615b8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; + +public class HealFilter extends SimpleChatFilter { + public HealFilter() { + super("^(?:You healed yourself for " + NUMBER + " health!|[a-zA-Z0-9_]{2,16} healed you for " + NUMBER + " health!)$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideHeal; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java new file mode 100644 index 00000000..454d7b78 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; + +public class ImplosionFilter extends SimpleChatFilter { + public ImplosionFilter() { + super("^Your Implosion hit " + NUMBER + " enem(?:y|ies) for " + NUMBER + " damage\\.$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideImplosion; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java new file mode 100644 index 00000000..afc15a2c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; + +public class MoltenWaveFilter extends SimpleChatFilter { + public MoltenWaveFilter() { + super("^Your Molten Wave hit " + NUMBER + " enem(?:y|ies) for " + NUMBER + " damage\\.$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideMoltenWave; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/ShowOffFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/ShowOffFilter.java new file mode 100644 index 00000000..a9c551fb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/ShowOffFilter.java @@ -0,0 +1,18 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; + +public class ShowOffFilter extends SimpleChatFilter { + private static final String[] SHOW_TYPES = { "is holding", "is wearing", "is friends with a", "has" }; + + public ShowOffFilter() { + super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + Constants.LEVEL_EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)[§f7]+ (?:" + String.join("|", SHOW_TYPES) + ") §8\\[(.+)§8\\]"); + } + + @Override + protected ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideShowOff; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java new file mode 100644 index 00000000..025b3dce --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java @@ -0,0 +1,17 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import net.minecraft.text.Text; + +import java.util.regex.Matcher; + +public abstract class SimpleChatFilter extends ChatPatternListener { + public SimpleChatFilter(String pattern) { + super(pattern); + } + + @Override + protected final boolean onMatch(Text message, Matcher matcher) { + return true; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilter.java new file mode 100644 index 00000000..57fac590 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilter.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; + +public class TeleportPadFilter extends SimpleChatFilter { + public TeleportPadFilter() { + super("^(Warped from the .* Teleport Pad to the .* Teleport Pad!" + + "|This Teleport Pad does not have a destination set!)$"); + } + + @Override + public ChatFilterResult state() { + return SkyblockerConfigManager.get().messages.hideTeleportPad; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/AttributeShards.java b/src/main/java/de/hysky/skyblocker/skyblock/item/AttributeShards.java new file mode 100644 index 00000000..ed650e26 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/AttributeShards.java @@ -0,0 +1,59 @@ +package de.hysky.skyblocker.skyblock.item; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; + +public class AttributeShards { + private static final Object2ObjectOpenHashMap ID_2_SHORT_NAME = new Object2ObjectOpenHashMap<>(); + + static { + //Weapons + ID_2_SHORT_NAME.put("arachno", "A"); + ID_2_SHORT_NAME.put("attack_speed", "AS"); + ID_2_SHORT_NAME.put("blazing", "BL"); + ID_2_SHORT_NAME.put("combo", "C"); + ID_2_SHORT_NAME.put("elite", "E"); + ID_2_SHORT_NAME.put("ender", "EN"); + ID_2_SHORT_NAME.put("ignition", "I"); + ID_2_SHORT_NAME.put("life_recovery", "LR"); + ID_2_SHORT_NAME.put("mana_steal", "MS"); + ID_2_SHORT_NAME.put("midas_touch", "MT"); + ID_2_SHORT_NAME.put("undead", "U"); + + //Swords & Bows + ID_2_SHORT_NAME.put("warrior", "W"); + ID_2_SHORT_NAME.put("deadeye", "DE"); + + //Armor or Equipment + ID_2_SHORT_NAME.put("arachno_resistance", "AR"); + ID_2_SHORT_NAME.put("blazing_resistance", "BR"); + ID_2_SHORT_NAME.put("breeze", "B"); + ID_2_SHORT_NAME.put("dominance", "D"); + ID_2_SHORT_NAME.put("ender_resistance", "ER"); + ID_2_SHORT_NAME.put("experience", "XP"); + ID_2_SHORT_NAME.put("fortitude", "F"); + ID_2_SHORT_NAME.put("life_regeneration", "HR"); //Health regeneration + ID_2_SHORT_NAME.put("lifeline", "L"); + ID_2_SHORT_NAME.put("magic_find", "MF"); + ID_2_SHORT_NAME.put("mana_pool", "MP"); + ID_2_SHORT_NAME.put("mana_regeneration", "MR"); + ID_2_SHORT_NAME.put("mending", "V"); //Vitality + ID_2_SHORT_NAME.put("speed", "S"); + ID_2_SHORT_NAME.put("undead_resistance", "UR"); + ID_2_SHORT_NAME.put("veteran", "V"); + + //Fishing Gear + ID_2_SHORT_NAME.put("blazing_fortune", "BF"); + ID_2_SHORT_NAME.put("fishing_experience", "FE"); + ID_2_SHORT_NAME.put("infection", "IF"); + ID_2_SHORT_NAME.put("double_hook", "DH"); + ID_2_SHORT_NAME.put("fisherman", "FM"); + ID_2_SHORT_NAME.put("fishing_speed", "FS"); + ID_2_SHORT_NAME.put("hunter", "H"); + ID_2_SHORT_NAME.put("trophy_hunter", "TH"); + + } + + public static String getShortName(String id) { + return ID_2_SHORT_NAME.getOrDefault(id, ""); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java b/src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java new file mode 100644 index 00000000..122ffe9b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java @@ -0,0 +1,235 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.*; +import net.minecraft.util.Identifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BackpackPreview { + private static final Logger LOGGER = LoggerFactory.getLogger(BackpackPreview.class); + private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png"); + private static final Pattern ECHEST_PATTERN = Pattern.compile("Ender Chest.*\\((\\d+)/\\d+\\)"); + private static final Pattern BACKPACK_PATTERN = Pattern.compile("Backpack.*\\(Slot #(\\d+)\\)"); + private static final int STORAGE_SIZE = 27; + + private static final Inventory[] storage = new Inventory[STORAGE_SIZE]; + private static final boolean[] dirty = new boolean[STORAGE_SIZE]; + + private static String loaded = ""; // uuid + sb profile currently loaded + private static Path save_dir = null; + + public static void init() { + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (screen instanceof HandledScreen handledScreen) { + updateStorage(handledScreen); + } + }); + } + + public static void tick() { + Utils.update(); // force update isOnSkyblock to prevent crash on disconnect + if (Utils.isOnSkyblock()) { + // save all dirty storages + saveStorage(); + // update save dir based on uuid and sb profile + String uuid = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", ""); + String profile = Utils.getProfile(); + if (profile != null && !profile.isEmpty()) { + save_dir = FabricLoader.getInstance().getConfigDir().resolve("skyblocker/backpack-preview/" + uuid + "/" + profile); + save_dir.toFile().mkdirs(); + if (loaded.equals(uuid + "/" + profile)) { + // mark currently opened storage as dirty + if (MinecraftClient.getInstance().currentScreen != null) { + String title = MinecraftClient.getInstance().currentScreen.getTitle().getString(); + int index = getStorageIndexFromTitle(title); + if (index != -1) dirty[index] = true; + } + } else { + // load storage again because uuid/profile changed + loaded = uuid + "/" + profile; + loadStorage(); + } + } + } + } + + public static void loadStorage() { + assert (save_dir != null); + for (int index = 0; index < STORAGE_SIZE; ++index) { + storage[index] = null; + dirty[index] = false; + File file = save_dir.resolve(index + ".nbt").toFile(); + if (file.isFile()) { + try { + NbtCompound root = NbtIo.read(file); + storage[index] = new DummyInventory(root); + } catch (Exception e) { + LOGGER.error("Failed to load backpack preview file: " + file.getName(), e); + } + } + } + } + + private static void saveStorage() { + assert (save_dir != null); + for (int index = 0; index < STORAGE_SIZE; ++index) { + if (dirty[index]) { + if (storage[index] != null) { + try { + NbtCompound root = new NbtCompound(); + NbtList list = new NbtList(); + for (int i = 9; i < storage[index].size(); ++i) { + ItemStack stack = storage[index].getStack(i); + NbtCompound item = new NbtCompound(); + if (stack.isEmpty()) { + item.put("id", NbtString.of("minecraft:air")); + item.put("Count", NbtInt.of(1)); + } else { + item.put("id", NbtString.of(stack.getItem().toString())); + item.put("Count", NbtInt.of(stack.getCount())); + item.put("tag", stack.getNbt()); + } + list.add(item); + } + root.put("list", list); + root.put("size", NbtInt.of(storage[index].size() - 9)); + NbtIo.write(root, save_dir.resolve(index + ".nbt").toFile()); + dirty[index] = false; + } catch (Exception e) { + LOGGER.error("Failed to save backpack preview file: " + index + ".nbt", e); + } + } + } + } + } + + public static void updateStorage(HandledScreen screen) { + String title = screen.getTitle().getString(); + int index = getStorageIndexFromTitle(title); + if (index != -1) { + storage[index] = screen.getScreenHandler().slots.get(0).inventory; + dirty[index] = true; + } + } + + public static boolean renderPreview(DrawContext context, int index, int mouseX, int mouseY) { + if (index >= 9 && index < 18) index -= 9; + else if (index >= 27 && index < 45) index -= 18; + else return false; + + if (storage[index] == null) return false; + int rows = (storage[index].size() - 9) / 9; + + Screen screen = MinecraftClient.getInstance().currentScreen; + if (screen == null) return false; + int x = mouseX + 184 >= screen.width ? mouseX - 188 : mouseX + 8; + int y = Math.max(0, mouseY - 16); + + RenderSystem.disableDepthTest(); + RenderSystem.setShaderTexture(0, TEXTURE); + context.drawTexture(TEXTURE, x, y, 0, 0, 176, 7); + for (int i = 0; i < rows; ++i) { + context.drawTexture(TEXTURE, x, y + i * 18 + 7, 0, 7, 176, 18); + } + context.drawTexture(TEXTURE, x, y + rows * 18 + 7, 0, 25, 176, 7); + RenderSystem.enableDepthTest(); + + MatrixStack matrices = context.getMatrices(); + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + for (int i = 9; i < storage[index].size(); ++i) { + int itemX = x + (i - 9) % 9 * 18 + 8; + int itemY = y + (i - 9) / 9 * 18 + 8; + matrices.push(); + matrices.translate(0, 0, 200); + context.drawItem(storage[index].getStack(i), itemX, itemY); + context.drawItemInSlot(textRenderer, storage[index].getStack(i), itemX, itemY); + matrices.pop(); + } + + return true; + } + + private static int getStorageIndexFromTitle(String title) { + Matcher echest = ECHEST_PATTERN.matcher(title); + if (echest.find()) return Integer.parseInt(echest.group(1)) - 1; + Matcher backpack = BACKPACK_PATTERN.matcher(title); + if (backpack.find()) return Integer.parseInt(backpack.group(1)) + 8; + return -1; + } +} + +class DummyInventory implements Inventory { + private final List stacks; + + public DummyInventory(NbtCompound root) { + stacks = new ArrayList<>(root.getInt("size") + 9); + for (int i = 0; i < 9; ++i) stacks.add(ItemStack.EMPTY); + root.getList("list", NbtCompound.COMPOUND_TYPE).forEach(item -> + stacks.add(ItemStack.fromNbt((NbtCompound) item)) + ); + } + + @Override + public int size() { + return stacks.size(); + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public ItemStack getStack(int slot) { + return stacks.get(slot); + } + + @Override + public ItemStack removeStack(int slot, int amount) { + return null; + } + + @Override + public ItemStack removeStack(int slot) { + return null; + } + + @Override + public void setStack(int slot, ItemStack stack) { + stacks.set(slot, stack); + } + + @Override + public void markDirty() { + } + + @Override + public boolean canPlayerUse(PlayerEntity player) { + return false; + } + + @Override + public void clear() { + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java new file mode 100644 index 00000000..2a6551c7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java @@ -0,0 +1,92 @@ +package de.hysky.skyblocker.skyblock.item; + +import de.hysky.skyblocker.mixin.accessor.DrawContextInvoker; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import it.unimi.dsi.fastutil.ints.IntObjectPair; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tooltip.HoveredTooltipPositioner; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class CompactorDeletorPreview { + /** + * The width and height in slots of the compactor/deletor + */ + private static final Map DIMENSIONS = Map.of( + "4000", IntIntPair.of(1, 1), + "5000", IntIntPair.of(1, 3), + "6000", IntIntPair.of(1, 7), + "7000", IntIntPair.of(2, 6) + ); + private static final IntIntPair DEFAULT_DIMENSION = IntIntPair.of(1, 6); + public static final Pattern NAME = Pattern.compile("PERSONAL_(?COMPACTOR|DELETOR)_(?\\d+)"); + private static final MinecraftClient client = MinecraftClient.getInstance(); + + public static boolean drawPreview(DrawContext context, ItemStack stack, String type, String size, int x, int y) { + List tooltips = Screen.getTooltipFromItem(client, stack); + int targetIndex = getTargetIndex(tooltips); + if (targetIndex == -1) return false; + + // Get items in compactor or deletor + NbtCompound nbt = stack.getNbt(); + if (nbt == null || !nbt.contains("ExtraAttributes", 10)) { + return false; + } + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + // Get the slots and their items from the nbt, which is in the format personal_compact_ or personal_deletor_ + List> slots = extraAttributes.getKeys().stream().filter(slot -> slot.contains(type.toLowerCase().substring(0, 7))).map(slot -> IntObjectPair.of(Integer.parseInt(slot.substring(17)), ItemRegistry.getItemStack(extraAttributes.getString(slot)))).toList(); + + List components = tooltips.stream().map(Text::asOrderedText).map(TooltipComponent::of).collect(Collectors.toList()); + IntIntPair dimensions = DIMENSIONS.getOrDefault(size, DEFAULT_DIMENSION); + + // If there are no items in compactor or deletor + if (slots.isEmpty()) { + int slotsCount = dimensions.leftInt() * dimensions.rightInt(); + components.add(targetIndex, TooltipComponent.of(Text.literal(slotsCount + (slotsCount == 1 ? " slot" : " slots")).formatted(Formatting.GRAY).asOrderedText())); + + ((DrawContextInvoker) context).invokeDrawTooltip(client.textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE); + return true; + } + + // Add the preview tooltip component + components.add(targetIndex, new CompactorPreviewTooltipComponent(slots, dimensions)); + + // Render accompanying text + components.add(targetIndex, TooltipComponent.of(Text.literal("Contents:").asOrderedText())); + if (extraAttributes.contains("PERSONAL_DELETOR_ACTIVE")) { + components.add(targetIndex, TooltipComponent.of(Text.literal("Active: ") + .append(extraAttributes.getBoolean("PERSONAL_DELETOR_ACTIVE") ? Text.literal("YES").formatted(Formatting.BOLD).formatted(Formatting.GREEN) : Text.literal("NO").formatted(Formatting.BOLD).formatted(Formatting.RED)).asOrderedText())); + } + ((DrawContextInvoker) context).invokeDrawTooltip(client.textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE); + return true; + } + + /** + * Finds the target index to insert the preview component, which is the second empty line + */ + private static int getTargetIndex(List tooltips) { + int targetIndex = -1; + int lineCount = 0; + for (int i = 0; i < tooltips.size(); i++) { + if (tooltips.get(i).getString().isEmpty()) { + lineCount += 1; + } + if (lineCount == 2) { + targetIndex = i; + break; + } + } + return targetIndex; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java new file mode 100644 index 00000000..f3634548 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java @@ -0,0 +1,54 @@ +package de.hysky.skyblocker.skyblock.item; + +import de.hysky.skyblocker.SkyblockerMod; +import it.unimi.dsi.fastutil.ints.IntIntPair; +import it.unimi.dsi.fastutil.ints.IntObjectPair; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; + +public class CompactorPreviewTooltipComponent implements TooltipComponent { + private static final Identifier INVENTORY_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png"); + private final Iterable> items; + private final IntIntPair dimensions; + + public CompactorPreviewTooltipComponent(Iterable> items, IntIntPair dimensions) { + this.items = items; + this.dimensions = dimensions; + } + + @Override + public int getHeight() { + return dimensions.leftInt() * 18 + 14; + } + + @Override + public int getWidth(TextRenderer textRenderer) { + return dimensions.rightInt() * 18 + 14; + } + + @Override + public void drawItems(TextRenderer textRenderer, int x, int y, DrawContext context) { + context.drawTexture(INVENTORY_TEXTURE, x, y, 0, 0, 7 + dimensions.rightInt() * 18, 7); + context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y, 169, 0, 7, 7); + + for (int i = 0; i < dimensions.leftInt(); i++) { + context.drawTexture(INVENTORY_TEXTURE, x, y + 7 + i * 18, 0, 7, 7, 18); + for (int j = 0; j < dimensions.rightInt(); j++) { + context.drawTexture(INVENTORY_TEXTURE, x + 7 + j * 18, y + 7 + i * 18, 7, 7, 18, 18); + } + context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y + 7 + i * 18, 169, 7, 7, 18); + } + context.drawTexture(INVENTORY_TEXTURE, x, y + 7 + dimensions.leftInt() * 18, 0, 25, 7 + dimensions.rightInt() * 18, 7); + context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y + 7 + dimensions.leftInt() * 18, 169, 25, 7, 7); + + for (IntObjectPair entry : items) { + int itemX = x + entry.leftInt() % dimensions.rightInt() * 18 + 8; + int itemY = y + entry.leftInt() / dimensions.rightInt() * 18 + 8; + context.drawItem(entry.right(), itemX, itemY); + context.drawItemInSlot(textRenderer, entry.right(), itemX, itemY); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java new file mode 100644 index 00000000..76042a81 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java @@ -0,0 +1,82 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.arguments.StringArgumentType; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.item.DyeableItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; + +public class CustomArmorDyeColors { + public static void init() { + ClientCommandRegistrationCallback.EVENT.register(CustomArmorDyeColors::registerCommands); + } + + private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("custom") + .then(ClientCommandManager.literal("dyeColor") + .executes(context -> customizeDyeColor(context.getSource(), null)) + .then(ClientCommandManager.argument("hexCode", StringArgumentType.string()) + .executes(context -> customizeDyeColor(context.getSource(), StringArgumentType.getString(context, "hexCode"))))))); + } + + @SuppressWarnings("SameReturnValue") + private static int customizeDyeColor(FabricClientCommandSource source, String hex) { + ItemStack heldItem = source.getPlayer().getMainHandStack(); + NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; + + if (hex != null && !isHexadecimalColor(hex)) { + source.sendError(Text.translatable("skyblocker.customDyeColors.invalidHex")); + return Command.SINGLE_SUCCESS; + } + + if (Utils.isOnSkyblock() && heldItem != null) { + if (heldItem.getItem() instanceof DyeableItem) { + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + if (itemUuid != null) { + Object2IntOpenHashMap customDyeColors = SkyblockerConfigManager.get().general.customDyeColors; + + if (hex == null) { + if (customDyeColors.containsKey(itemUuid)) { + customDyeColors.removeInt(itemUuid); + SkyblockerConfigManager.save(); + source.sendFeedback(Text.translatable("skyblocker.customDyeColors.removed")); + } else { + source.sendFeedback(Text.translatable("skyblocker.customDyeColors.neverHad")); + } + } else { + customDyeColors.put(itemUuid, Integer.decode("0x" + hex.replace("#", "")).intValue()); + SkyblockerConfigManager.save(); + source.sendFeedback(Text.translatable("skyblocker.customDyeColors.added")); + } + } else { + source.sendError(Text.translatable("skyblocker.customDyeColors.noItemUuid")); + } + } + } else { + source.sendError(Text.translatable("skyblocker.customDyeColors.notDyeable")); + return Command.SINGLE_SUCCESS; + } + } else { + source.sendError(Text.translatable("skyblocker.customDyeColors.unableToSetColor")); + } + + return Command.SINGLE_SUCCESS; + } + + private static boolean isHexadecimalColor(String s) { + return s.replace("#", "").chars().allMatch(c -> "0123456789ABCDEFabcdef".indexOf(c) >= 0) && s.replace("#", "").length() == 6; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java new file mode 100644 index 00000000..b8fa0797 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java @@ -0,0 +1,154 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.suggestion.SuggestionProvider; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.Utils; +import dev.isxander.yacl3.config.v2.api.SerialEntry; +import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +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.client.network.ClientPlayerEntity; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.CommandSource; +import net.minecraft.command.argument.IdentifierArgumentType; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.trim.ArmorTrim; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtOps; +import net.minecraft.registry.*; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Optional; + +public class CustomArmorTrims { + private static final Logger LOGGER = LoggerFactory.getLogger(CustomArmorTrims.class); + public static final Object2ObjectOpenHashMap> TRIMS_CACHE = new Object2ObjectOpenHashMap<>(); + private static boolean trimsInitialized = false; + + public static void init() { + SkyblockEvents.JOIN.register(CustomArmorTrims::initializeTrimCache); + ClientCommandRegistrationCallback.EVENT.register(CustomArmorTrims::registerCommand); + } + + private static void initializeTrimCache() { + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (trimsInitialized || player == null) { + return; + } + try { + TRIMS_CACHE.clear(); + DynamicRegistryManager registryManager = player.networkHandler.getRegistryManager(); + for (Identifier material : registryManager.get(RegistryKeys.TRIM_MATERIAL).getIds()) { + for (Identifier pattern : registryManager.get(RegistryKeys.TRIM_PATTERN).getIds()) { + NbtCompound compound = new NbtCompound(); + compound.putString("material", material.toString()); + compound.putString("pattern", pattern.toString()); + + ArmorTrim trim = ArmorTrim.CODEC.parse(RegistryOps.of(NbtOps.INSTANCE, registryManager), compound).resultOrPartial(LOGGER::error).orElse(null); + + // Something went terribly wrong + if (trim == null) throw new IllegalStateException("Trim shouldn't be null! [" + "\"" + material + "\",\"" + pattern + "\"]"); + + TRIMS_CACHE.put(new ArmorTrimId(material, pattern), Optional.of(trim)); + } + } + + LOGGER.info("[Skyblocker] Successfully cached all armor trims!"); + trimsInitialized = true; + } catch (Exception e) { + LOGGER.error("[Skyblocker] Encountered an exception while caching armor trims", e); + } + } + + private static void registerCommand(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("custom") + .then(ClientCommandManager.literal("armorTrim") + .executes(context -> customizeTrim(context.getSource(), null, null)) + .then(ClientCommandManager.argument("material", IdentifierArgumentType.identifier()) + .suggests(getIdSuggestionProvider(RegistryKeys.TRIM_MATERIAL)) + .executes(context -> customizeTrim(context.getSource(), context.getArgument("material", Identifier.class), null)) + .then(ClientCommandManager.argument("pattern", IdentifierArgumentType.identifier()) + .suggests(getIdSuggestionProvider(RegistryKeys.TRIM_PATTERN)) + .executes(context -> customizeTrim(context.getSource(), context.getArgument("material", Identifier.class), context.getArgument("pattern", Identifier.class)))))))); + } + + @NotNull + private static SuggestionProvider getIdSuggestionProvider(RegistryKey> registryKey) { + return (context, builder) -> context.getSource().listIdSuggestions(registryKey, CommandSource.SuggestedIdType.ELEMENTS, builder, context); + } + + @SuppressWarnings("SameReturnValue") + private static int customizeTrim(FabricClientCommandSource source, Identifier material, Identifier pattern) { + ItemStack heldItem = source.getPlayer().getMainHandStack(); + NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; + + if (Utils.isOnSkyblock() && heldItem != null) { + if (heldItem.getItem() instanceof ArmorItem) { + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + if (itemUuid != null) { + Object2ObjectOpenHashMap customArmorTrims = SkyblockerConfigManager.get().general.customArmorTrims; + + if (material == null && pattern == null) { + if (customArmorTrims.containsKey(itemUuid)) { + customArmorTrims.remove(itemUuid); + SkyblockerConfigManager.save(); + source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.removed")); + } else { + source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.neverHad")); + } + } else { + // Ensure that the material & trim are valid + ArmorTrimId trimId = new ArmorTrimId(material, pattern); + if (TRIMS_CACHE.get(trimId) == null) { + source.sendError(Text.translatable("skyblocker.customArmorTrims.invalidMaterialOrPattern")); + + return Command.SINGLE_SUCCESS; + } + + customArmorTrims.put(itemUuid, trimId); + SkyblockerConfigManager.save(); + source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.added")); + } + } else { + source.sendError(Text.translatable("skyblocker.customArmorTrims.noItemUuid")); + } + } + } else { + source.sendError(Text.translatable("skyblocker.customArmorTrims.notAnArmorPiece")); + return Command.SINGLE_SUCCESS; + } + } else { + source.sendError(Text.translatable("skyblocker.customArmorTrims.unableToSetTrim")); + } + + return Command.SINGLE_SUCCESS; + } + + public record ArmorTrimId(@SerialEntry Identifier material, @SerialEntry Identifier pattern) implements Pair { + @Override + public Identifier left() { + return material(); + } + + @Override + public Identifier right() { + return pattern(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java new file mode 100644 index 00000000..5fbff253 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java @@ -0,0 +1,74 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.command.argument.TextArgumentType; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; + +public class CustomItemNames { + public static void init() { + ClientCommandRegistrationCallback.EVENT.register(CustomItemNames::registerCommands); + } + + private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("custom") + .then(ClientCommandManager.literal("renameItem") + .executes(context -> renameItem(context.getSource(), null)) + .then(ClientCommandManager.argument("textComponent", TextArgumentType.text()) + .executes(context -> renameItem(context.getSource(), context.getArgument("textComponent", Text.class))))))); + } + + @SuppressWarnings("SameReturnValue") + private static int renameItem(FabricClientCommandSource source, Text text) { + ItemStack heldItem = source.getPlayer().getMainHandStack(); + NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + if (itemUuid != null) { + Object2ObjectOpenHashMap customItemNames = SkyblockerConfigManager.get().general.customItemNames; + + if (text == null) { + if (customItemNames.containsKey(itemUuid)) { + //Remove custom item name when the text argument isn't passed + customItemNames.remove(itemUuid); + SkyblockerConfigManager.save(); + source.sendFeedback(Text.translatable("skyblocker.customItemNames.removed")); + } else { + source.sendFeedback(Text.translatable("skyblocker.customItemNames.neverHad")); + } + } else { + //If the text is provided then set the item's custom name to it + + //Set italic to false if it hasn't been changed (or was already false) + Style currentStyle = text.getStyle(); + ((MutableText) text).setStyle(currentStyle.withItalic((currentStyle.isItalic() ? true : false))); + + customItemNames.put(itemUuid, text); + SkyblockerConfigManager.save(); + source.sendFeedback(Text.translatable("skyblocker.customItemNames.added")); + } + } else { + source.sendError(Text.translatable("skyblocker.customItemNames.noItemUuid")); + } + } else { + source.sendError(Text.translatable("skyblocker.customItemNames.unableToSetName")); + } + + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java new file mode 100644 index 00000000..9c1fa6ad --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java @@ -0,0 +1,115 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.google.common.collect.ImmutableList; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.events.ClientPlayerBlockBreakEvent; +import de.hysky.skyblocker.utils.ItemUtils; +import net.fabricmc.fabric.api.event.player.UseItemCallback; +import net.minecraft.block.BlockState; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.util.Hand; +import net.minecraft.util.TypedActionResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import java.util.HashMap; +import java.util.Map; + +public class ItemCooldowns { + private static final String JUNGLE_AXE_ID = "JUNGLE_AXE"; + private static final String TREECAPITATOR_ID = "TREECAPITATOR_AXE"; + private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; + private static final ImmutableList BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); + + private static final Map ITEM_COOLDOWNS = new HashMap<>(); + + public static void init() { + ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); + UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); + } + + public static void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state) { + if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return; + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId == null) return; + + if (state.isIn(BlockTags.LOGS)) { + if (usedItemId.equals(JUNGLE_AXE_ID)) { + if (!isOnCooldown(JUNGLE_AXE_ID)) { + ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); + } + } else if (usedItemId.equals(TREECAPITATOR_ID)) { + if (!isOnCooldown(TREECAPITATOR_ID)) { + ITEM_COOLDOWNS.put(TREECAPITATOR_ID, new CooldownEntry(2000)); + } + } + } + } + + private static TypedActionResult onItemInteract(PlayerEntity player, World world, Hand hand) { + if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); + + String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); + if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { + if (!isOnCooldown(GRAPPLING_HOOK_ID) && !isWearingBatArmor(player)) { + ITEM_COOLDOWNS.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); + } + } + + return TypedActionResult.pass(ItemStack.EMPTY); + } + + public static boolean isOnCooldown(ItemStack itemStack) { + return isOnCooldown(ItemUtils.getItemId(itemStack)); + } + + private static boolean isOnCooldown(String itemId) { + if (ITEM_COOLDOWNS.containsKey(itemId)) { + CooldownEntry cooldownEntry = ITEM_COOLDOWNS.get(itemId); + if (cooldownEntry.isOnCooldown()) { + return true; + } else { + ITEM_COOLDOWNS.remove(itemId); + return false; + } + } + + return false; + } + + public static CooldownEntry getItemCooldownEntry(ItemStack itemStack) { + return ITEM_COOLDOWNS.get(ItemUtils.getItemId(itemStack)); + } + + private static boolean isWearingBatArmor(PlayerEntity player) { + for (ItemStack stack : player.getArmorItems()) { + String itemId = ItemUtils.getItemId(stack); + if (!BAT_ARMOR_IDS.contains(itemId)) { + return false; + } + } + return true; + } + + public record CooldownEntry(int cooldown, long startTime) { + public CooldownEntry(int cooldown) { + this(cooldown, System.currentTimeMillis()); + } + + public boolean isOnCooldown() { + return (this.startTime + this.cooldown) > System.currentTimeMillis(); + } + + public long getRemainingCooldown() { + long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); + return Math.max(time, 0); + } + + public float getRemainingCooldownPercent() { + return this.isOnCooldown() ? (float) this.getRemainingCooldown() / cooldown : 0.0f; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java new file mode 100644 index 00000000..d73e1545 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java @@ -0,0 +1,75 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; + +public class ItemProtection { + + public static void init() { + ClientCommandRegistrationCallback.EVENT.register(ItemProtection::registerCommand); + } + + public static boolean isItemProtected(ItemStack stack) { + if (stack == null || stack.isEmpty()) return false; + + NbtCompound nbt = stack.getNbt(); + + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : ""; + + return SkyblockerConfigManager.get().general.protectedItems.contains(itemUuid); + } + + return false; + } + + private static void registerCommand(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("protectItem") + .executes(context -> protectMyItem(context.getSource())))); + } + + private static int protectMyItem(FabricClientCommandSource source) { + ItemStack heldItem = source.getPlayer().getMainHandStack(); + NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; + + if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; + + if (itemUuid != null) { + ObjectOpenHashSet protectedItems = SkyblockerConfigManager.get().general.protectedItems; + + if (!protectedItems.contains(itemUuid)) { + protectedItems.add(itemUuid); + SkyblockerConfigManager.save(); + + source.sendFeedback(Text.translatable("skyblocker.itemProtection.added", heldItem.getName())); + } else { + protectedItems.remove(itemUuid); + SkyblockerConfigManager.save(); + + source.sendFeedback(Text.translatable("skyblocker.itemProtection.removed", heldItem.getName())); + } + } else { + source.sendFeedback(Text.translatable("skyblocker.itemProtection.noItemUuid")); + } + } else { + source.sendFeedback(Text.translatable("skyblocker.itemProtection.unableToProtect")); + } + + return Command.SINGLE_SUCCESS; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java new file mode 100644 index 00000000..0af64bd9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemRarityBackgrounds.java @@ -0,0 +1,109 @@ +package de.hysky.skyblocker.skyblock.item; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableMap; +import com.mojang.blaze3d.systems.RenderSystem; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.texture.Sprite; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class ItemRarityBackgrounds { + private static final Identifier RARITY_BG_TEX = new Identifier(SkyblockerMod.NAMESPACE, "item_rarity_background"); + private static final Supplier SPRITE = () -> MinecraftClient.getInstance().getGuiAtlasManager().getSprite(RARITY_BG_TEX); + private static final ImmutableMap LORE_RARITIES = ImmutableMap.ofEntries( + Map.entry("ADMIN", SkyblockItemRarity.ADMIN), + Map.entry("SPECIAL", SkyblockItemRarity.SPECIAL), //Very special is the same color so this will cover it + Map.entry("DIVINE", SkyblockItemRarity.DIVINE), + Map.entry("MYTHIC", SkyblockItemRarity.MYTHIC), + Map.entry("LEGENDARY", SkyblockItemRarity.LEGENDARY), + Map.entry("LEGENJERRY", SkyblockItemRarity.LEGENDARY), + Map.entry("EPIC", SkyblockItemRarity.EPIC), + Map.entry("RARE", SkyblockItemRarity.RARE), + Map.entry("UNCOMMON", SkyblockItemRarity.UNCOMMON), + Map.entry("COMMON", SkyblockItemRarity.COMMON) + ); + private static final Int2ReferenceOpenHashMap CACHE = new Int2ReferenceOpenHashMap<>(); + + public static void init() { + //Clear the cache every 5 minutes, ints are very compact! + Scheduler.INSTANCE.scheduleCyclic(CACHE::clear, 4800); + + //Clear cache after a screen where items can be upgraded in rarity closes + ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + String title = screen.getTitle().getString(); + + if (Utils.isOnSkyblock() && (title.equals("The Hex") || title.equals("Craft Item") || title.equals("Anvil") || title.equals("Reforge Anvil"))) { + ScreenEvents.remove(screen).register(screen1 -> CACHE.clear()); + } + }); + } + + public static void tryDraw(ItemStack stack, DrawContext context, int x, int y) { + MinecraftClient client = MinecraftClient.getInstance(); + + if (client.player != null) { + SkyblockItemRarity itemRarity = getItemRarity(stack, client.player); + + if (itemRarity != null) draw(context, x, y, itemRarity); + } + } + + private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { + if (stack == null || stack.isEmpty()) return null; + + int hashCode = 0; + NbtCompound nbt = stack.getNbt(); + + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + String itemUuid = extraAttributes.getString("uuid"); + + //If the item has an uuid, then use the hash code of the uuid otherwise use the identity hash code of the stack + hashCode = itemUuid.isEmpty() ? System.identityHashCode(stack) : itemUuid.hashCode(); + } + + if (CACHE.containsKey(hashCode)) return CACHE.get(hashCode); + + List tooltip = stack.getTooltip(player, TooltipContext.BASIC); + String[] stringifiedTooltip = tooltip.stream().map(Text::getString).toArray(String[]::new); + + for (String rarityString : LORE_RARITIES.keySet()) { + if (Arrays.stream(stringifiedTooltip).anyMatch(line -> line.contains(rarityString))) { + SkyblockItemRarity rarity = LORE_RARITIES.get(rarityString); + + CACHE.put(hashCode, rarity); + return rarity; + } + } + + CACHE.put(hashCode, null); + return null; + } + + private static void draw(DrawContext context, int x, int y, SkyblockItemRarity rarity) { + //Enable blending to handle HUD translucency + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + + context.drawSprite(x, y, 0, 16, 16, SPRITE.get(), rarity.r, rarity.g, rarity.b, SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgroundsOpacity); + + RenderSystem.disableBlend(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java new file mode 100644 index 00000000..05767558 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java @@ -0,0 +1,443 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Http; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.http.HttpHeaders; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; + +public class PriceInfoTooltip { + private static final Logger LOGGER = LoggerFactory.getLogger(PriceInfoTooltip.class.getName()); + private static final MinecraftClient client = MinecraftClient.getInstance(); + private static JsonObject npcPricesJson; + private static JsonObject bazaarPricesJson; + private static JsonObject oneDayAvgPricesJson; + private static JsonObject threeDayAvgPricesJson; + private static JsonObject lowestPricesJson; + private static JsonObject isMuseumJson; + private static JsonObject motesPricesJson; + private static boolean nullMsgSend = false; + private final static Gson gson = new Gson(); + private static final Map apiAddresses; + private static long npcHash = 0; + private static long museumHash = 0; + private static long motesHash = 0; + + public static void onInjectTooltip(ItemStack stack, TooltipContext context, List lines) { + if (!Utils.isOnSkyblock() || client.player == null) return; + + String name = getInternalNameFromNBT(stack, false); + String internalID = getInternalNameFromNBT(stack, true); + String neuName = name; + if (name == null || internalID == null) return; + + if(name.startsWith("ISSHINY_")){ + name = "SHINY_" + internalID; + neuName = internalID; + } + + int count = stack.getCount(); + boolean bazaarOpened = lines.stream().anyMatch(each -> each.getString().contains("Buy price:") || each.getString().contains("Sell price:")); + + if (SkyblockerConfigManager.get().general.itemTooltip.enableNPCPrice) { + if (npcPricesJson == null) { + nullWarning(); + } else if (npcPricesJson.has(internalID)) { + lines.add(Text.literal(String.format("%-21s", "NPC Price:")) + .formatted(Formatting.YELLOW) + .append(getCoinsMessage(npcPricesJson.get(internalID).getAsDouble(), count))); + } + } + + if (SkyblockerConfigManager.get().general.itemTooltip.enableMotesPrice && Utils.isInTheRift()) { + if (motesPricesJson == null) { + nullWarning(); + } else if (motesPricesJson.has(internalID)) { + lines.add(Text.literal(String.format("%-20s", "Motes Price:")) + .formatted(Formatting.LIGHT_PURPLE) + .append(getMotesMessage(motesPricesJson.get(internalID).getAsInt(), count))); + } + } + + boolean bazaarExist = false; + + if (SkyblockerConfigManager.get().general.itemTooltip.enableBazaarPrice && !bazaarOpened) { + if (bazaarPricesJson == null) { + nullWarning(); + } else if (bazaarPricesJson.has(name)) { + JsonObject getItem = bazaarPricesJson.getAsJsonObject(name); + lines.add(Text.literal(String.format("%-18s", "Bazaar buy Price:")) + .formatted(Formatting.GOLD) + .append(getItem.get("buyPrice").isJsonNull() + ? Text.literal("No data").formatted(Formatting.RED) + : getCoinsMessage(getItem.get("buyPrice").getAsDouble(), count))); + lines.add(Text.literal(String.format("%-19s", "Bazaar sell Price:")) + .formatted(Formatting.GOLD) + .append(getItem.get("sellPrice").isJsonNull() + ? Text.literal("No data").formatted(Formatting.RED) + : getCoinsMessage(getItem.get("sellPrice").getAsDouble(), count))); + bazaarExist = true; + } + } + + // bazaarOpened & bazaarExist check for lbin, because Skytils keeps some bazaar item data in lbin api + boolean lbinExist = false; + if (SkyblockerConfigManager.get().general.itemTooltip.enableLowestBIN && !bazaarOpened && !bazaarExist) { + if (lowestPricesJson == null) { + nullWarning(); + } else if (lowestPricesJson.has(name)) { + lines.add(Text.literal(String.format("%-19s", "Lowest BIN Price:")) + .formatted(Formatting.GOLD) + .append(getCoinsMessage(lowestPricesJson.get(name).getAsDouble(), count))); + lbinExist = true; + } + } + + if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) { + if (threeDayAvgPricesJson == null || oneDayAvgPricesJson == null) { + nullWarning(); + } else { + /* + We are skipping check average prices for potions, runes + and enchanted books because there is no data for their in API. + */ + switch (internalID) { + case "PET" -> { + neuName = neuName.replaceAll("LVL_\\d*_", ""); + String[] parts = neuName.split("_"); + String type = parts[0]; + neuName = neuName.replaceAll(type + "_", ""); + neuName = neuName + "-" + type; + neuName = neuName.replace("UNCOMMON", "1") + .replace("COMMON", "0") + .replace("RARE", "2") + .replace("EPIC", "3") + .replace("LEGENDARY", "4") + .replace("MYTHIC", "5") + .replace("-", ";"); + } + case "RUNE" -> neuName = neuName.replaceAll("_(?!.*_)", ";"); + case "POTION" -> neuName = ""; + case "ATTRIBUTE_SHARD" -> + neuName = internalID + "+" + neuName.replace("SHARD-", "").replaceAll("_(?!.*_)", ";"); + default -> neuName = neuName.replace(":", "-"); + } + + if (!neuName.isEmpty() && lbinExist) { + SkyblockerConfig.Average type = SkyblockerConfigManager.get().general.itemTooltip.avg; + + // "No data" line because of API not keeping old data, it causes NullPointerException + if (type == SkyblockerConfig.Average.ONE_DAY || type == SkyblockerConfig.Average.BOTH) { + lines.add( + Text.literal(String.format("%-19s", "1 Day Avg. Price:")) + .formatted(Formatting.GOLD) + .append(oneDayAvgPricesJson.get(neuName) == null + ? Text.literal("No data").formatted(Formatting.RED) + : getCoinsMessage(oneDayAvgPricesJson.get(neuName).getAsDouble(), count) + ) + ); + } + if (type == SkyblockerConfig.Average.THREE_DAY || type == SkyblockerConfig.Average.BOTH) { + lines.add( + Text.literal(String.format("%-19s", "3 Day Avg. Price:")) + .formatted(Formatting.GOLD) + .append(threeDayAvgPricesJson.get(neuName) == null + ? Text.literal("No data").formatted(Formatting.RED) + : getCoinsMessage(threeDayAvgPricesJson.get(neuName).getAsDouble(), count) + ) + ); + } + } + } + } + + if (SkyblockerConfigManager.get().general.itemTooltip.enableMuseumDate && !bazaarOpened) { + if (isMuseumJson == null) { + nullWarning(); + } else { + String timestamp = getTimestamp(stack); + + if (isMuseumJson.has(internalID)) { + String itemCategory = isMuseumJson.get(internalID).getAsString(); + String format = switch (itemCategory) { + case "Weapons" -> "%-18s"; + case "Armor" -> "%-19s"; + default -> "%-20s"; + }; + lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")")) + .formatted(Formatting.LIGHT_PURPLE) + .append(Text.literal(timestamp).formatted(Formatting.RED))); + } else if (!timestamp.isEmpty()) { + lines.add(Text.literal(String.format("%-21s", "Obtained: ")) + .formatted(Formatting.LIGHT_PURPLE) + .append(Text.literal(timestamp).formatted(Formatting.RED))); + } + } + } + } + + private static void nullWarning() { + if (!nullMsgSend && client.player != null) { + client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false); + nullMsgSend = true; + } + } + + public static NbtCompound getItemNBT(ItemStack stack) { + if (stack == null) return null; + return stack.getNbt(); + } + + /** + * this method converts the "timestamp" variable into the same date format as Hypixel represents it in the museum. + * Currently, there are two types of timestamps the legacy which is built like this + * "dd/MM/yy hh:mm" ("25/04/20 16:38") and the current which is built like this + * "MM/dd/yy hh:mm aa" ("12/24/20 11:08 PM"). Since Hypixel transforms the two formats into one format without + * taking into account of their formats, we do the same. The final result looks like this + * "MMMM dd, yyyy" (December 24, 2020). + * Since the legacy format has a 25 as "month" SimpleDateFormat converts the 25 into 2 years and 1 month and makes + * "25/04/20 16:38" -> "January 04, 2022" instead of "April 25, 2020". + * This causes the museum rank to be much worse than it should be. + * + * @param stack the item under the pointer + * @return if the item have a "Timestamp" it will be shown formated on the tooltip + */ + public static String getTimestamp(ItemStack stack) { + NbtCompound tag = getItemNBT(stack); + + if (tag != null && tag.contains("ExtraAttributes", 10)) { + NbtCompound ea = tag.getCompound("ExtraAttributes"); + + if (ea.contains("timestamp", 8)) { + SimpleDateFormat nbtFormat = new SimpleDateFormat("MM/dd/yy"); + + try { + Date date = nbtFormat.parse(ea.getString("timestamp")); + SimpleDateFormat skyblockerFormat = new SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH); + return skyblockerFormat.format(date); + } catch (ParseException e) { + LOGGER.warn("[Skyblocker-tooltip] getTimestamp", e); + } + } + } + + return ""; + } + + public static String getInternalNameFromNBT(ItemStack stack, boolean internalIDOnly) { + NbtCompound tag = getItemNBT(stack); + if (tag == null || !tag.contains("ExtraAttributes", 10)) { + return null; + } + NbtCompound ea = tag.getCompound("ExtraAttributes"); + + if (!ea.contains("id", 8)) { + return null; + } + String internalName = ea.getString("id"); + + if (internalIDOnly) { + return internalName; + } + + // Transformation to API format. + if (ea.contains("is_shiny")){ + return "ISSHINY_" + internalName; + } + + switch (internalName) { + case "ENCHANTED_BOOK" -> { + if (ea.contains("enchantments")) { + NbtCompound enchants = ea.getCompound("enchantments"); + Optional firstEnchant = enchants.getKeys().stream().findFirst(); + String enchant = firstEnchant.orElse(""); + return "ENCHANTMENT_" + enchant.toUpperCase(Locale.ENGLISH) + "_" + enchants.getInt(enchant); + } + } + case "PET" -> { + if (ea.contains("petInfo")) { + JsonObject petInfo = gson.fromJson(ea.getString("petInfo"), JsonObject.class); + return "LVL_1_" + petInfo.get("tier").getAsString() + "_" + petInfo.get("type").getAsString(); + } + } + case "POTION" -> { + String enhanced = ea.contains("enhanced") ? "_ENHANCED" : ""; + String extended = ea.contains("extended") ? "_EXTENDED" : ""; + String splash = ea.contains("splash") ? "_SPLASH" : ""; + if (ea.contains("potion") && ea.contains("potion_level")) { + return (ea.getString("potion") + "_" + internalName + "_" + ea.getInt("potion_level") + + enhanced + extended + splash).toUpperCase(Locale.ENGLISH); + } + } + case "RUNE" -> { + if (ea.contains("runes")) { + NbtCompound runes = ea.getCompound("runes"); + Optional firstRunes = runes.getKeys().stream().findFirst(); + String rune = firstRunes.orElse(""); + return rune.toUpperCase(Locale.ENGLISH) + "_RUNE_" + runes.getInt(rune); + } + } + case "ATTRIBUTE_SHARD" -> { + if (ea.contains("attributes")) { + NbtCompound shards = ea.getCompound("attributes"); + Optional firstShards = shards.getKeys().stream().findFirst(); + String shard = firstShards.orElse(""); + return internalName + "-" + shard.toUpperCase(Locale.ENGLISH) + "_" + shards.getInt(shard); + } + } + } + return internalName; + } + + + private static Text getCoinsMessage(double price, int count) { + // Format the price string once + String priceString = String.format(Locale.ENGLISH, "%1$,.1f", price); + + // If count is 1, return a simple message + if (count == 1) { + return Text.literal(priceString + " Coins").formatted(Formatting.DARK_AQUA); + } + + // If count is greater than 1, include the "each" information + String priceStringTotal = String.format(Locale.ENGLISH, "%1$,.1f", price * count); + MutableText message = Text.literal(priceStringTotal + " Coins ").formatted(Formatting.DARK_AQUA); + message.append(Text.literal("(" + priceString + " each)").formatted(Formatting.GRAY)); + + return message; + } + + private static Text getMotesMessage(int price, int count) { + float motesMultiplier = SkyblockerConfigManager.get().locations.rift.mcGrubberStacks * 0.05f + 1; + + // Calculate the total price + int totalPrice = price * count; + String totalPriceString = String.format(Locale.ENGLISH, "%1$,.1f", totalPrice * motesMultiplier); + + // If count is 1, return a simple message + if (count == 1) { + return Text.literal(totalPriceString.replace(".0", "") + " Motes").formatted(Formatting.DARK_AQUA); + } + + // If count is greater than 1, include the "each" information + String eachPriceString = String.format(Locale.ENGLISH, "%1$,.1f", price * motesMultiplier); + MutableText message = Text.literal(totalPriceString.replace(".0", "") + " Motes ").formatted(Formatting.DARK_AQUA); + message.append(Text.literal("(" + eachPriceString.replace(".0", "") + " each)").formatted(Formatting.GRAY)); + + return message; + } + + // 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 = -1; + + public static void init() { + Scheduler.INSTANCE.scheduleCyclic(() -> { + if (!Utils.isOnSkyblock() && 0 < minute++) { + nullMsgSend = false; + return; + } + + List> futureList = new ArrayList<>(); + if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) { + SkyblockerConfig.Average type = SkyblockerConfigManager.get().general.itemTooltip.avg; + + if (type == SkyblockerConfig.Average.BOTH || oneDayAvgPricesJson == null || threeDayAvgPricesJson == null || minute % 5 == 0) { + futureList.add(CompletableFuture.runAsync(() -> { + oneDayAvgPricesJson = downloadPrices("1 day avg"); + threeDayAvgPricesJson = downloadPrices("3 day avg"); + })); + } else if (type == SkyblockerConfig.Average.ONE_DAY) { + futureList.add(CompletableFuture.runAsync(() -> oneDayAvgPricesJson = downloadPrices("1 day avg"))); + } else if (type == SkyblockerConfig.Average.THREE_DAY) { + futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg"))); + } + } + if (SkyblockerConfigManager.get().general.itemTooltip.enableLowestBIN || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator) + futureList.add(CompletableFuture.runAsync(() -> lowestPricesJson = downloadPrices("lowest bins"))); + + if (SkyblockerConfigManager.get().general.itemTooltip.enableBazaarPrice || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator) + futureList.add(CompletableFuture.runAsync(() -> bazaarPricesJson = downloadPrices("bazaar"))); + + if (SkyblockerConfigManager.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null) + futureList.add(CompletableFuture.runAsync(() -> npcPricesJson = downloadPrices("npc"))); + + if (SkyblockerConfigManager.get().general.itemTooltip.enableMuseumDate && isMuseumJson == null) + futureList.add(CompletableFuture.runAsync(() -> isMuseumJson = downloadPrices("museum"))); + + if (SkyblockerConfigManager.get().general.itemTooltip.enableMotesPrice && motesPricesJson == null) + futureList.add(CompletableFuture.runAsync(() -> motesPricesJson = downloadPrices("motes"))); + + minute++; + CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])) + .whenComplete((unused, throwable) -> nullMsgSend = false); + }, 1200); + } + + private static JsonObject downloadPrices(String type) { + try { + String url = apiAddresses.get(type); + + if (type.equals("npc") || type.equals("museum") || type.equals("motes")) { + HttpHeaders headers = Http.sendHeadRequest(url); + long combinedHash = Http.getEtag(headers).hashCode() + Http.getLastModified(headers).hashCode(); + + switch (type) { + case "npc": if (npcHash == combinedHash) return npcPricesJson; else npcHash = combinedHash; + case "museum": if (museumHash == combinedHash) return isMuseumJson; else museumHash = combinedHash; + case "motes": if (motesHash == combinedHash) return motesPricesJson; else motesHash = combinedHash; + } + } + + String apiResponse = Http.sendGetRequest(url); + + return new Gson().fromJson(apiResponse, JsonObject.class); + } catch (Exception e) { + LOGGER.warn("[Skyblocker] Failed to download " + type + " prices!", e); + return null; + } + } + + public static JsonObject getBazaarPrices() { + return bazaarPricesJson; + } + + public static JsonObject getLBINPrices() { + return lowestPricesJson; + } + + static { + apiAddresses = new HashMap<>(); + apiAddresses.put("1 day avg", "https://moulberry.codes/auction_averages_lbin/1day.json"); + apiAddresses.put("3 day avg", "https://moulberry.codes/auction_averages_lbin/3day.json"); + apiAddresses.put("bazaar", "https://hysky.de/api/bazaar"); + apiAddresses.put("lowest bins", "https://hysky.de/api/auctions/lowestbins"); + apiAddresses.put("npc", "https://hysky.de/api/npcprice"); + apiAddresses.put("museum", "https://hysky.de/api/museum"); + apiAddresses.put("motes", "https://hysky.de/api/motesprice"); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockItemRarity.java b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockItemRarity.java new file mode 100644 index 00000000..08cc5377 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/SkyblockItemRarity.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.skyblock.item; + +import net.minecraft.util.Formatting; + +public enum SkyblockItemRarity { + ADMIN(Formatting.DARK_RED), + VERY_SPECIAL(Formatting.RED), + SPECIAL(Formatting.RED), + DIVINE(Formatting.AQUA), + MYTHIC(Formatting.LIGHT_PURPLE), + LEGENDARY(Formatting.GOLD), + EPIC(Formatting.DARK_PURPLE), + RARE(Formatting.BLUE), + UNCOMMON(Formatting.GREEN), + COMMON(Formatting.WHITE); + + public final float r; + public final float g; + public final float b; + + SkyblockItemRarity(Formatting formatting) { + @SuppressWarnings("DataFlowIssue") + int rgb = formatting.getColorValue(); + + this.r = ((rgb >> 16) & 0xFF) / 255f; + this.g = ((rgb >> 8) & 0xFF) / 255f; + this.b = (rgb & 0xFF) / 255f; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java b/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java new file mode 100644 index 00000000..3ab478d0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java @@ -0,0 +1,56 @@ +package de.hysky.skyblocker.skyblock.item; + +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import net.minecraft.util.Util; +import org.lwjgl.glfw.GLFW; + +import java.util.concurrent.CompletableFuture; + +public class WikiLookup { + public static KeyBinding wikiLookup; + static final MinecraftClient client = MinecraftClient.getInstance(); + static String id; + + public static void init() { + wikiLookup = KeyBindingHelper.registerKeyBinding(new KeyBinding( + "key.wikiLookup", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_F4, + "key.categories.skyblocker" + )); + } + + public static String getSkyblockId(Slot slot) { + //Grabbing the skyblock NBT data + ItemStack selectedStack = slot.getStack(); + NbtCompound nbt = selectedStack.getSubNbt("ExtraAttributes"); + if (nbt != null) { + id = nbt.getString("id"); + } + return id; + } + + public static void openWiki(Slot slot) { + if (Utils.isOnSkyblock()) { + id = getSkyblockId(slot); + try { + String wikiLink = ItemRegistry.getWikiLink(id); + CompletableFuture.runAsync(() -> Util.getOperatingSystem().open(wikiLink)); + } catch (IndexOutOfBoundsException | IllegalStateException e) { + e.printStackTrace(); + if (client.player != null) + client.player.sendMessage(Text.of("Error while retrieving wiki article..."), false); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java new file mode 100644 index 00000000..488c5f48 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java @@ -0,0 +1,341 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import java.util.Map; + +public class ItemFixerUpper { + private final static String[] ANVIL_VARIANTS = { + "minecraft:anvil", + "minecraft:chipped_anvil", + "minecraft:damaged_anvil" + }; + + private final static String[] COAL_VARIANTS = { + "minecraft:coal", + "minecraft:charcoal" + }; + + private final static String[] COBBLESTONE_WALL_VARIANTS = { + "minecraft:cobblestone_wall", + "minecraft:mossy_cobblestone_wall" + }; + + private final static String[] COOKED_FISH_VARIANTS = { + "minecraft:cooked_cod", + "minecraft:cooked_salmon" + }; + + private final static String[] DIRT_VARIANTS = { + "minecraft:dirt", + "minecraft:coarse_dirt", + "minecraft:podzol" + }; + + private final static String[] DOUBLE_PLANT_VARIANTS = { + "minecraft:sunflower", + "minecraft:lilac", + "minecraft:tall_grass", + "minecraft:large_fern", + "minecraft:rose_bush", + "minecraft:peony" + }; + + private final static String[] DYE_VARIANTS = { + "minecraft:ink_sac", + "minecraft:red_dye", + "minecraft:green_dye", + "minecraft:cocoa_beans", + "minecraft:lapis_lazuli", + "minecraft:purple_dye", + "minecraft:cyan_dye", + "minecraft:light_gray_dye", + "minecraft:gray_dye", + "minecraft:pink_dye", + "minecraft:lime_dye", + "minecraft:yellow_dye", + "minecraft:light_blue_dye", + "minecraft:magenta_dye", + "minecraft:orange_dye", + "minecraft:bone_meal" + }; + + private final static String[] FISH_VARIANTS = { + "minecraft:cod", + "minecraft:salmon", + "minecraft:tropical_fish", + "minecraft:pufferfish" + }; + + private final static String[] GOLDEN_APPLE_VARIANTS = { + "minecraft:golden_apple", + "minecraft:enchanted_golden_apple" + }; + + private final static String[] LOG_VARIANTS = { + "minecraft:oak_log", + "minecraft:spruce_log", + "minecraft:birch_log", + "minecraft:jungle_log", + "minecraft:oak_wood", + "minecraft:spruce_wood", + "minecraft:birch_wood", + "minecraft:jungle_wood", + }; + + private final static String[] LOG2_VARIANTS = { + "minecraft:acacia_log", + "minecraft:dark_oak_log", + "minecraft:acacia_wood", + "minecraft:dark_oak_wood" + }; + + private final static String[] MONSTER_EGG_VARIANTS = { + "minecraft:infested_stone", + "minecraft:infested_cobblestone", + "minecraft:infested_stone_bricks", + "minecraft:infested_mossy_stone_bricks", + "minecraft:infested_cracked_stone_bricks", + "minecraft:infested_chiseled_stone_bricks" + }; + + private final static String[] PRISMARINE_VARIANTS = { + "minecraft:prismarine", + "minecraft:prismarine_bricks", + "minecraft:dark_prismarine" + }; + + private final static String[] QUARTZ_BLOCK_VARIANTS = { + "minecraft:quartz_block", + "minecraft:chiseled_quartz_block", + "minecraft:quartz_pillar" + }; + + private final static String[] RED_FLOWER_VARIANTS = { + "minecraft:poppy", + "minecraft:blue_orchid", + "minecraft:allium", + "minecraft:azure_bluet", + "minecraft:red_tulip", + "minecraft:orange_tulip", + "minecraft:white_tulip", + "minecraft:pink_tulip", + "minecraft:oxeye_daisy" + }; + + private final static String[] SAND_VARIANTS = { + "minecraft:sand", + "minecraft:red_sand" + }; + + private final static String[] SKULL_VARIANTS = { + "minecraft:skeleton_skull", + "minecraft:wither_skeleton_skull", + "minecraft:zombie_head", + "minecraft:player_head", + "minecraft:creeper_head" + }; + + private final static String[] SPONGE_VARIANTS = { + "minecraft:sponge", + "minecraft:wet_sponge" + }; + + private final static String[] STONE_VARIANTS = { + "minecraft:stone", + "minecraft:granite", + "minecraft:polished_granite", + "minecraft:diorite", + "minecraft:polished_diorite", + "minecraft:andesite", + "minecraft:polished_andesite" + }; + + private final static String[] STONE_SLAB_VARIANTS = { + "minecraft:smooth_stone_slab", + "minecraft:sandstone_slab", + "minecraft:petrified_oak_slab", + "minecraft:cobblestone_slab", + "minecraft:brick_slab", + "minecraft:stone_brick_slab", + "minecraft:nether_brick_slab", + "minecraft:quartz_slab" + }; + + private final static String[] STONEBRICK_VARIANTS = { + "minecraft:stone_bricks", + "minecraft:mossy_stone_bricks", + "minecraft:cracked_stone_bricks", + "minecraft:chiseled_stone_bricks" + }; + + private final static String[] TALLGRASS_VARIANTS = { + "minecraft:dead_bush", + "minecraft:grass", + "minecraft:fern" + }; + + private final static Map SPAWN_EGG_VARIANTS = Map.ofEntries( + //This entry 0 is technically not right but Hypixel decided to make it polar bear so well we use that + Map.entry(0, "minecraft:polar_bear_spawn_egg"), + Map.entry(50, "minecraft:creeper_spawn_egg"), + Map.entry(51, "minecraft:skeleton_spawn_egg"), + Map.entry(52, "minecraft:spider_spawn_egg"), + Map.entry(54, "minecraft:zombie_spawn_egg"), + Map.entry(55, "minecraft:slime_spawn_egg"), + Map.entry(56, "minecraft:ghast_spawn_egg"), + Map.entry(57, "minecraft:zombified_piglin_spawn_egg"), + Map.entry(58, "minecraft:enderman_spawn_egg"), + Map.entry(59, "minecraft:cave_spider_spawn_egg"), + Map.entry(60, "minecraft:silverfish_spawn_egg"), + Map.entry(61, "minecraft:blaze_spawn_egg"), + Map.entry(62, "minecraft:magma_cube_spawn_egg"), + Map.entry(65, "minecraft:bat_spawn_egg"), + Map.entry(66, "minecraft:witch_spawn_egg"), + Map.entry(67, "minecraft:endermite_spawn_egg"), + Map.entry(68, "minecraft:guardian_spawn_egg"), + Map.entry(90, "minecraft:pig_spawn_egg"), + Map.entry(91, "minecraft:sheep_spawn_egg"), + Map.entry(92, "minecraft:cow_spawn_egg"), + Map.entry(93, "minecraft:chicken_spawn_egg"), + Map.entry(94, "minecraft:squid_spawn_egg"), + Map.entry(95, "minecraft:wolf_spawn_egg"), + Map.entry(96, "minecraft:mooshroom_spawn_egg"), + Map.entry(98, "minecraft:ocelot_spawn_egg"), + Map.entry(100, "minecraft:horse_spawn_egg"), + Map.entry(101, "minecraft:rabbit_spawn_egg"), + Map.entry(120, "minecraft:villager_spawn_egg") + ); + + private final static String[] SANDSTONE_VARIANTS = { + ":", + ":chiseled_", + ":cut_" + }; + + private final static String[] COLOR_VARIANTS = { + ":white_", + ":orange_", + ":magenta_", + ":light_blue_", + ":yellow_", + ":lime_", + ":pink_", + ":gray_", + ":light_gray_", + ":cyan_", + ":purple_", + ":blue_", + ":brown_", + ":green_", + ":red_", + ":black_" + }; + + private final static String[] WOOD_VARIANTS = { + ":oak_", + ":spruce_", + ":birch_", + ":jungle_", + ":acacia_", + ":dark_oak_" + }; + + //this is the map of all renames + private final static Map RENAMED = Map.ofEntries( + Map.entry("minecraft:bed", "minecraft:red_bed"), + Map.entry("minecraft:boat", "minecraft:oak_boat"), + Map.entry("minecraft:brick_block", "minecraft:bricks"), + Map.entry("minecraft:deadbush", "minecraft:dead_bush"), + Map.entry("minecraft:fence_gate", "minecraft:oak_fence_gate"), + Map.entry("minecraft:fence", "minecraft:oak_fence"), + Map.entry("minecraft:firework_charge", "minecraft:firework_star"), + Map.entry("minecraft:fireworks", "minecraft:firework_rocket"), + Map.entry("minecraft:golden_rail", "minecraft:powered_rail"), + Map.entry("minecraft:grass", "minecraft:grass_block"), + Map.entry("minecraft:hardened_clay", "minecraft:terracotta"), + Map.entry("minecraft:lit_pumpkin", "minecraft:jack_o_lantern"), + Map.entry("minecraft:melon_block", "minecraft:melon"), + Map.entry("minecraft:melon", "minecraft:melon_slice"), + Map.entry("minecraft:mob_spawner", "minecraft:spawner"), + Map.entry("minecraft:nether_brick", "minecraft:nether_bricks"), + Map.entry("minecraft:netherbrick", "minecraft:nether_brick"), + Map.entry("minecraft:noteblock", "minecraft:note_block"), + Map.entry("minecraft:piston_extension", "minecraft:moving_piston"), + Map.entry("minecraft:portal", "minecraft:nether_portal"), + Map.entry("minecraft:pumpkin", "minecraft:carved_pumpkin"), + Map.entry("minecraft:quartz_ore", "minecraft:nether_quartz_ore"), + Map.entry("minecraft:record_11", "minecraft:music_disc_11"), + Map.entry("minecraft:record_13", "minecraft:music_disc_13"), + Map.entry("minecraft:record_blocks", "minecraft:music_disc_blocks"), + Map.entry("minecraft:record_cat", "minecraft:music_disc_cat"), + Map.entry("minecraft:record_chirp", "minecraft:music_disc_chirp"), + Map.entry("minecraft:record_far", "minecraft:music_disc_far"), + Map.entry("minecraft:record_mall", "minecraft:music_disc_mall"), + Map.entry("minecraft:record_mellohi", "minecraft:music_disc_mellohi"), + Map.entry("minecraft:record_stal", "minecraft:music_disc_stal"), + Map.entry("minecraft:record_strad", "minecraft:music_disc_strad"), + Map.entry("minecraft:record_wait", "minecraft:music_disc_wait"), + Map.entry("minecraft:record_ward", "minecraft:music_disc_ward"), + Map.entry("minecraft:red_nether_brick", "minecraft:red_nether_bricks"), + Map.entry("minecraft:reeds", "minecraft:sugar_cane"), + Map.entry("minecraft:sign", "minecraft:oak_sign"), + Map.entry("minecraft:slime", "minecraft:slime_block"), + Map.entry("minecraft:snow_layer", "minecraft:snow"), + Map.entry("minecraft:snow", "minecraft:snow_block"), + Map.entry("minecraft:speckled_melon", "minecraft:glistering_melon_slice"), + Map.entry("minecraft:stone_slab2", "minecraft:red_sandstone_slab"), + Map.entry("minecraft:stone_stairs", "minecraft:cobblestone_stairs"), + Map.entry("minecraft:trapdoor", "minecraft:oak_trapdoor"), + Map.entry("minecraft:waterlily", "minecraft:lily_pad"), + Map.entry("minecraft:web", "minecraft:cobweb"), + Map.entry("minecraft:wooden_button", "minecraft:oak_button"), + Map.entry("minecraft:wooden_door", "minecraft:oak_door"), + Map.entry("minecraft:wooden_pressure_plate", "minecraft:oak_pressure_plate"), + Map.entry("minecraft:yellow_flower", "minecraft:dandelion") + ); + + //TODO : Add mushroom block variants + //i'll do it later because it isn't used and unlike the other, it's not just a rename or a separate, it's a separate and a merge + + public static String convertItemId(String id, int damage) { + return switch (id) { + //all the case are simple separate + case "minecraft:anvil" -> ANVIL_VARIANTS[damage]; + case "minecraft:coal" -> COAL_VARIANTS[damage]; + case "minecraft:cobblestone_wall" -> COBBLESTONE_WALL_VARIANTS[damage]; + case "minecraft:cooked_fish" -> COOKED_FISH_VARIANTS[damage]; + case "minecraft:dirt" -> DIRT_VARIANTS[damage]; + case "minecraft:double_plant" -> DOUBLE_PLANT_VARIANTS[damage]; + case "minecraft:dye" -> DYE_VARIANTS[damage]; + case "minecraft:fish" -> FISH_VARIANTS[damage]; + case "minecraft:golden_apple" -> GOLDEN_APPLE_VARIANTS[damage]; + case "minecraft:log" -> LOG_VARIANTS[damage]; + case "minecraft:log2" -> LOG2_VARIANTS[damage]; + case "minecraft:monster_egg" -> MONSTER_EGG_VARIANTS[damage]; + case "minecraft:prismarine" -> PRISMARINE_VARIANTS[damage]; + case "minecraft:quartz_block" -> QUARTZ_BLOCK_VARIANTS[damage]; + case "minecraft:red_flower" -> RED_FLOWER_VARIANTS[damage]; + case "minecraft:sand" -> SAND_VARIANTS[damage]; + case "minecraft:skull" -> SKULL_VARIANTS[damage]; + case "minecraft:sponge" -> SPONGE_VARIANTS[damage]; + case "minecraft:stone" -> STONE_VARIANTS[damage]; + case "minecraft:stone_slab" -> STONE_SLAB_VARIANTS[damage]; + case "minecraft:stonebrick" -> STONEBRICK_VARIANTS[damage]; + case "minecraft:tallgrass" -> TALLGRASS_VARIANTS[damage]; + //we use a Map from int to str instead of an array because numbers are not consecutive + case "minecraft:spawn_egg" -> SPAWN_EGG_VARIANTS.get(damage); + //when we use the generalized variant we need to replaceFirst + case "minecraft:sandstone", "minecraft:red_sandstone" -> id.replaceFirst(":", SANDSTONE_VARIANTS[damage]); + //to use the general color variants we need to reverse the order because Minecraft decided so for some reason + case "minecraft:banner" -> id.replaceFirst(":", COLOR_VARIANTS[15 - damage]); + case "minecraft:carpet", "minecraft:stained_glass", "minecraft:stained_glass_pane", "minecraft:wool" -> id.replaceFirst(":", COLOR_VARIANTS[damage]); + //for the terracotta we replace the whole name by the color and append "terracotta" at the end + case "minecraft:stained_hardened_clay" -> id.replaceFirst(":stained_hardened_clay", COLOR_VARIANTS[damage]) + "terracotta"; + //for the wooden slab we need to remove the "wooden_" prefix, but otherwise it's the same, so I just combined them anyway + case "minecraft:leaves", "minecraft:planks", "minecraft:sapling", "minecraft:wooden_slab" -> id.replaceFirst(":(?:wooden_)?", WOOD_VARIANTS[damage]); + //here we replace the 2 by nothing to remove it as it's not needed anymore + case "minecraft:leaves2" -> id.replaceFirst(":", WOOD_VARIANTS[damage + 4]).replaceFirst("2", ""); + //the default case is just a rename or no change + default -> RENAMED.getOrDefault(id, id); + }; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java new file mode 100644 index 00000000..afdcaca8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListWidget.java @@ -0,0 +1,102 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.mixin.accessor.RecipeBookWidgetAccessor; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.screen.AbstractRecipeScreenHandler; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +@Environment(value = EnvType.CLIENT) +public class ItemListWidget extends RecipeBookWidget { + private int parentWidth; + private int parentHeight; + private int leftOffset; + private TextFieldWidget searchField; + private SearchResultsWidget results; + + public ItemListWidget() { + super(); + } + + public void updateSearchResult() { + this.results.updateSearchResult(((RecipeBookWidgetAccessor) this).getSearchText()); + } + + @Override + public void initialize(int parentWidth, int parentHeight, MinecraftClient client, boolean narrow, AbstractRecipeScreenHandler craftingScreenHandler) { + super.initialize(parentWidth, parentHeight, client, narrow, craftingScreenHandler); + this.parentWidth = parentWidth; + this.parentHeight = parentHeight; + this.leftOffset = narrow ? 0 : 86; + this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); + int x = (this.parentWidth - 147) / 2 - this.leftOffset; + int y = (this.parentHeight - 166) / 2; + if (ItemRegistry.filesImported) { + this.results = new SearchResultsWidget(this.client, x, y); + this.updateSearchResult(); + } + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + if (this.isOpen()) { + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(0.0D, 0.0D, 100.0D); + RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); + this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); + int i = (this.parentWidth - 147) / 2 - this.leftOffset; + int j = (this.parentHeight - 166) / 2; + context.drawTexture(TEXTURE, i, j, 1, 1, 147, 166); + this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); + + if (!ItemRegistry.filesImported && !this.searchField.isFocused() && this.searchField.getText().isEmpty()) { + Text hintText = (Text.literal("Loading...")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); + context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1); + } else if (!this.searchField.isFocused() && this.searchField.getText().isEmpty()) { + Text hintText = (Text.translatable("gui.recipebook.search_hint")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); + context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1); + } else { + this.searchField.render(context, mouseX, mouseY, delta); + } + if (ItemRegistry.filesImported) { + if (results == null) { + int x = (this.parentWidth - 147) / 2 - this.leftOffset; + int y = (this.parentHeight - 166) / 2; + this.results = new SearchResultsWidget(this.client, x, y); + } + this.updateSearchResult(); + this.results.render(context, mouseX, mouseY, delta); + } + matrices.pop(); + } + } + + @Override + public void drawTooltip(DrawContext context, int x, int y, int mouseX, int mouseY) { + if (this.isOpen() && ItemRegistry.filesImported && results != null) { + this.results.drawTooltip(context, mouseX, mouseY); + } + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (this.isOpen() && this.client.player != null && !this.client.player.isSpectator() && ItemRegistry.filesImported && this.searchField != null && results != null) { + if (this.searchField.mouseClicked(mouseX, mouseY, button)) { + this.results.closeRecipeView(); + this.searchField.setFocused(true); + return true; + } else { + this.searchField.setFocused(false); + return this.results.mouseClicked(mouseX, mouseY, button); + } + } else return false; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRegistry.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRegistry.java new file mode 100644 index 00000000..edfeccc0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemRegistry.java @@ -0,0 +1,137 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.hysky.skyblocker.utils.NEURepo; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public class ItemRegistry { + protected static final Logger LOGGER = LoggerFactory.getLogger(ItemRegistry.class); + protected static final Path ITEM_LIST_DIR = NEURepo.LOCAL_REPO_DIR.resolve("items"); + + protected static final List items = new ArrayList<>(); + protected static final Map itemsMap = new HashMap<>(); + protected static final List recipes = new ArrayList<>(); + public static final MinecraftClient client = MinecraftClient.getInstance(); + public static boolean filesImported = false; + + public static void init() { + NEURepo.runAsyncAfterLoad(ItemStackBuilder::loadPetNums); + NEURepo.runAsyncAfterLoad(ItemRegistry::importItemFiles); + } + + private static void importItemFiles() { + List jsonObjs = new ArrayList<>(); + + File dir = ITEM_LIST_DIR.toFile(); + File[] files = dir.listFiles(); + if (files == null) { + return; + } + for (File file : files) { + Path path = ITEM_LIST_DIR.resolve(file.getName()); + try { + String fileContent = Files.readString(path); + jsonObjs.add(JsonParser.parseString(fileContent).getAsJsonObject()); + } catch (Exception e) { + LOGGER.error("Failed to read file " + path, e); + } + } + + for (JsonObject jsonObj : jsonObjs) { + String internalName = jsonObj.get("internalname").getAsString(); + ItemStack itemStack = ItemStackBuilder.parseJsonObj(jsonObj); + items.add(itemStack); + itemsMap.put(internalName, itemStack); + } + for (JsonObject jsonObj : jsonObjs) + if (jsonObj.has("recipe")) { + recipes.add(SkyblockCraftingRecipe.fromJsonObject(jsonObj)); + } + + items.sort((lhs, rhs) -> { + String lhsInternalName = getInternalName(lhs); + String lhsFamilyName = lhsInternalName.replaceAll(".\\d+$", ""); + String rhsInternalName = getInternalName(rhs); + String rhsFamilyName = rhsInternalName.replaceAll(".\\d+$", ""); + if (lhsFamilyName.equals(rhsFamilyName)) { + if (lhsInternalName.length() != rhsInternalName.length()) + return lhsInternalName.length() - rhsInternalName.length(); + else return lhsInternalName.compareTo(rhsInternalName); + } + return lhsFamilyName.compareTo(rhsFamilyName); + }); + filesImported = true; + } + + public static String getWikiLink(String internalName) { + try { + String fileContent = Files.readString(ITEM_LIST_DIR.resolve(internalName + ".json")); + JsonObject fileJson = JsonParser.parseString(fileContent).getAsJsonObject(); + //TODO optional official or unofficial wiki link + try { + return fileJson.get("info").getAsJsonArray().get(1).getAsString(); + } catch (IndexOutOfBoundsException e) { + return fileJson.get("info").getAsJsonArray().get(0).getAsString(); + } + } catch (IOException | NullPointerException e) { + LOGGER.error("Failed to read item file " + internalName + ".json", e); + if (client.player != null) { + client.player.sendMessage(Text.of("Can't locate a wiki article for this item..."), false); + } + return null; + } + } + + public static List getRecipes(String internalName) { + List result = new ArrayList<>(); + for (SkyblockCraftingRecipe recipe : recipes) + if (getInternalName(recipe.result).equals(internalName)) result.add(recipe); + for (SkyblockCraftingRecipe recipe : recipes) + for (ItemStack ingredient : recipe.grid) + if (!ingredient.getItem().equals(Items.AIR) && getInternalName(ingredient).equals(internalName)) { + result.add(recipe); + break; + } + return result; + } + + public static Stream getRecipesStream() { + return recipes.stream(); + } + + public static Stream getItemsStream() { + return items.stream(); + } + + /** + * Get Internal name of an ItemStack + * + * @param itemStack ItemStack to get internal name from + * @return internal name of the given ItemStack + */ + public static String getInternalName(ItemStack itemStack) { + if (itemStack.getNbt() == null) return ""; + return itemStack.getNbt().getCompound("ExtraAttributes").getString("id"); + } + + public static ItemStack getItemStack(String internalName) { + return itemsMap.get(internalName); + } +} + diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java new file mode 100644 index 00000000..24146c64 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java @@ -0,0 +1,154 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.hysky.skyblocker.utils.NEURepo; +import net.minecraft.item.FireworkRocketItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.*; +import net.minecraft.text.Text; +import net.minecraft.util.Pair; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ItemStackBuilder { + private final static Path PETNUMS_PATH = NEURepo.LOCAL_REPO_DIR.resolve("constants/petnums.json"); + private static JsonObject petNums; + + public static void loadPetNums() { + try { + petNums = JsonParser.parseString(Files.readString(PETNUMS_PATH)).getAsJsonObject(); + } catch (Exception e) { + ItemRegistry.LOGGER.error("Failed to load petnums.json"); + } + } + + public static ItemStack parseJsonObj(JsonObject obj) { + String internalName = obj.get("internalname").getAsString(); + + List> injectors = new ArrayList<>(petData(internalName)); + + NbtCompound root = new NbtCompound(); + root.put("Count", NbtByte.of((byte)1)); + + String id = obj.get("itemid").getAsString(); + int damage = obj.get("damage").getAsInt(); + root.put("id", NbtString.of(ItemFixerUpper.convertItemId(id, damage))); + + NbtCompound tag = new NbtCompound(); + root.put("tag", tag); + + NbtCompound extra = new NbtCompound(); + tag.put("ExtraAttributes", extra); + extra.put("id", NbtString.of(internalName)); + + NbtCompound display = new NbtCompound(); + tag.put("display", display); + + String name = injectData(obj.get("displayname").getAsString(), injectors); + display.put("Name", NbtString.of(Text.Serializer.toJson(Text.of(name)))); + + NbtList lore = new NbtList(); + display.put("Lore", lore); + obj.get("lore").getAsJsonArray().forEach(el -> + lore.add(NbtString.of(Text.Serializer.toJson(Text.of(injectData(el.getAsString(), injectors))))) + ); + + String nbttag = obj.get("nbttag").getAsString(); + // add skull texture + Matcher skullUuid = Pattern.compile("(?<=SkullOwner:\\{)Id:\"(.{36})\"").matcher(nbttag); + Matcher skullTexture = Pattern.compile("(?<=Properties:\\{textures:\\[0:\\{Value:)\"(.+?)\"").matcher(nbttag); + if (skullUuid.find() && skullTexture.find()) { + NbtCompound skullOwner = new NbtCompound(); + tag.put("SkullOwner", skullOwner); + UUID uuid = UUID.fromString(skullUuid.group(1)); + skullOwner.put("Id", NbtHelper.fromUuid(uuid)); + skullOwner.put("Name", NbtString.of(internalName)); + + NbtCompound properties = new NbtCompound(); + skullOwner.put("Properties", properties); + NbtList textures = new NbtList(); + properties.put("textures", textures); + NbtCompound texture = new NbtCompound(); + textures.add(texture); + texture.put("Value", NbtString.of(skullTexture.group(1))); + } + // add leather armor dye color + Matcher colorMatcher = Pattern.compile("color:(\\d+)").matcher(nbttag); + if (colorMatcher.find()) { + NbtInt color = NbtInt.of(Integer.parseInt(colorMatcher.group(1))); + display.put("color", color); + } + // add enchantment glint + if (nbttag.contains("ench:")) { + NbtList enchantments = new NbtList(); + enchantments.add(new NbtCompound()); + tag.put("Enchantments", enchantments); + } + + // Add firework star color + Matcher explosionColorMatcher = Pattern.compile("\\{Explosion:\\{(?:Type:[0-9a-z]+,)?Colors:\\[(?[0-9]+)\\]\\}").matcher(nbttag); + if (explosionColorMatcher.find()) { + NbtCompound explosion = new NbtCompound(); + + explosion.putInt("Type", FireworkRocketItem.Type.SMALL_BALL.getId()); //Forget about the actual ball type because it probably doesn't matter + explosion.putIntArray("Colors", new int[] { Integer.parseInt(explosionColorMatcher.group("color")) }); + tag.put("Explosion", explosion); + } + + return ItemStack.fromNbt(root); + } + + // TODO: fix stats for GOLDEN_DRAGON (lv1 -> lv200) + private static List> petData(String internalName) { + List> list = new ArrayList<>(); + + String petName = internalName.split(";")[0]; + if (!internalName.contains(";") || !petNums.has(petName)) return list; + + list.add(new Pair<>("\\{LVL\\}", "1 ➡ 100")); + + final String[] rarities = { + "COMMON", + "UNCOMMON", + "RARE", + "EPIC", + "LEGENDARY", + "MYTHIC" + }; + String rarity = rarities[Integer.parseInt(internalName.split(";")[1])]; + JsonObject data = petNums.get(petName).getAsJsonObject().get(rarity).getAsJsonObject(); + + JsonObject statNumsMin = data.get("1").getAsJsonObject().get("statNums").getAsJsonObject(); + JsonObject statNumsMax = data.get("100").getAsJsonObject().get("statNums").getAsJsonObject(); + Set> entrySet = statNumsMin.entrySet(); + for (Map.Entry entry : entrySet) { + String key = entry.getKey(); + String left = "\\{" + key+ "\\}"; + String right = statNumsMin.get(key).getAsString() + " ➡ " + statNumsMax.get(key).getAsString(); + list.add(new Pair<>(left, right)); + } + + JsonArray otherNumsMin = data.get("1").getAsJsonObject().get("otherNums").getAsJsonArray(); + JsonArray otherNumsMax = data.get("100").getAsJsonObject().get("otherNums").getAsJsonArray(); + for (int i = 0; i < otherNumsMin.size(); ++i) { + String left = "\\{" + i + "\\}"; + String right = otherNumsMin.get(i).getAsString() + " ➡ " + otherNumsMax.get(i).getAsString(); + list.add(new Pair<>(left, right)); + } + + return list; + } + + private static String injectData(String string, List> injectors) { + for (Pair injector : injectors) + string = string.replaceAll(injector.getLeft(), injector.getRight()); + return string; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ResultButtonWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ResultButtonWidget.java new file mode 100644 index 00000000..814611e5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ResultButtonWidget.java @@ -0,0 +1,65 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import java.util.List; +import java.util.ArrayList; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.OrderedText; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +public class ResultButtonWidget extends ClickableWidget { + private static final Identifier BACKGROUND_TEXTURE = new Identifier("recipe_book/slot_craftable"); + + protected ItemStack itemStack = null; + + public ResultButtonWidget(int x, int y) { + super(x, y, 25, 25, Text.of("")); + } + + protected void setItemStack(ItemStack itemStack) { + this.active = !itemStack.getItem().equals(Items.AIR); + this.visible = true; + this.itemStack = itemStack; + } + + protected void clearItemStack() { + this.visible = false; + this.itemStack = null; + } + + @Override + public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) { + MinecraftClient client = MinecraftClient.getInstance(); + // this.drawTexture(matrices, this.x, this.y, 29, 206, this.width, this.height); + context.drawGuiTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), this.getWidth(), this.getHeight()); + // client.getItemRenderer().renderInGui(this.itemStack, this.x + 4, this.y + 4); + context.drawItem(this.itemStack, this.getX() + 4, this.getY() + 4); + // client.getItemRenderer().renderGuiItemOverlay(client.textRenderer, itemStack, this.x + 4, this.y + 4); + context.drawItemInSlot(client.textRenderer, itemStack, this.getX() + 4, this.getY() + 4); + } + + public void renderTooltip(DrawContext context, int mouseX, int mouseY) { + MinecraftClient client = MinecraftClient.getInstance(); + List tooltip = Screen.getTooltipFromItem(client, this.itemStack); + List orderedTooltip = new ArrayList<>(); + + for(int i = 0; i < tooltip.size(); i++) { + orderedTooltip.add(tooltip.get(i).asOrderedText()); + } + + client.currentScreen.setTooltip(orderedTooltip); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + // TODO Auto-generated method stub + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java new file mode 100644 index 00000000..eedf695e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java @@ -0,0 +1,228 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Drawable; +import net.minecraft.client.gui.screen.ButtonTextures; +import net.minecraft.client.gui.widget.ToggleButtonWidget; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jetbrains.annotations.Nullable; + +public class SearchResultsWidget implements Drawable { + private static final ButtonTextures PAGE_FORWARD_TEXTURES = new ButtonTextures(new Identifier("recipe_book/page_forward"), new Identifier("recipe_book/page_forward_highlighted")); + private static final ButtonTextures PAGE_BACKWARD_TEXTURES = new ButtonTextures(new Identifier("recipe_book/page_backward"), new Identifier("recipe_book/page_backward_highlighted")); + private static final int COLS = 5; + private static final int MAX_TEXT_WIDTH = 124; + private static final String ELLIPSIS = "..."; + private static final Pattern FORMATTING_CODE_PATTERN = Pattern.compile("(?i)§[0-9A-FK-OR]"); + + private final MinecraftClient client; + private final int parentX; + private final int parentY; + + private final List searchResults = new ArrayList<>(); + private List recipeResults = new ArrayList<>(); + private String searchText = null; + private final List resultButtons = new ArrayList<>(); + private final ToggleButtonWidget nextPageButton; + private final ToggleButtonWidget prevPageButton; + private int currentPage = 0; + private int pageCount = 0; + private boolean displayRecipes = false; + + public SearchResultsWidget(MinecraftClient client, int parentX, int parentY) { + this.client = client; + this.parentX = parentX; + this.parentY = parentY; + int gridX = parentX + 11; + int gridY = parentY + 31; + int rows = 4; + for (int i = 0; i < rows; ++i) + for (int j = 0; j < COLS; ++j) { + int x = gridX + j * 25; + int y = gridY + i * 25; + resultButtons.add(new ResultButtonWidget(x, y)); + } + this.nextPageButton = new ToggleButtonWidget(parentX + 93, parentY + 137, 12, 17, false); + this.nextPageButton.setTextures(PAGE_FORWARD_TEXTURES); + this.prevPageButton = new ToggleButtonWidget(parentX + 38, parentY + 137, 12, 17, true); + this.prevPageButton.setTextures(PAGE_BACKWARD_TEXTURES); + } + + public void closeRecipeView() { + this.currentPage = 0; + this.pageCount = (this.searchResults.size() - 1) / resultButtons.size() + 1; + this.displayRecipes = false; + this.updateButtons(); + } + + protected void updateSearchResult(String searchText) { + if (!searchText.equals(this.searchText)) { + this.searchText = searchText; + this.searchResults.clear(); + for (ItemStack entry : ItemRegistry.items) { + String name = entry.getName().toString().toLowerCase(Locale.ENGLISH); + if (entry.getNbt() == null) { + continue; + } + String disp = entry.getNbt().getCompound("display").toString().toLowerCase(Locale.ENGLISH); + if (name.contains(this.searchText) || disp.contains(this.searchText)) + this.searchResults.add(entry); + } + this.currentPage = 0; + this.pageCount = (this.searchResults.size() - 1) / resultButtons.size() + 1; + this.displayRecipes = false; + this.updateButtons(); + } + } + + private void updateButtons() { + if (this.displayRecipes) { + SkyblockCraftingRecipe recipe = this.recipeResults.get(this.currentPage); + for (ResultButtonWidget button : resultButtons) + button.clearItemStack(); + resultButtons.get(5).setItemStack(recipe.grid.get(0)); + resultButtons.get(6).setItemStack(recipe.grid.get(1)); + resultButtons.get(7).setItemStack(recipe.grid.get(2)); + resultButtons.get(10).setItemStack(recipe.grid.get(3)); + resultButtons.get(11).setItemStack(recipe.grid.get(4)); + resultButtons.get(12).setItemStack(recipe.grid.get(5)); + resultButtons.get(15).setItemStack(recipe.grid.get(6)); + resultButtons.get(16).setItemStack(recipe.grid.get(7)); + resultButtons.get(17).setItemStack(recipe.grid.get(8)); + resultButtons.get(14).setItemStack(recipe.result); + } else { + for (int i = 0; i < resultButtons.size(); ++i) { + int index = this.currentPage * resultButtons.size() + i; + if (index < this.searchResults.size()) { + resultButtons.get(i).setItemStack(this.searchResults.get(index)); + } else { + resultButtons.get(i).clearItemStack(); + } + } + } + this.prevPageButton.active = this.currentPage > 0; + this.nextPageButton.active = this.currentPage < this.pageCount - 1; + } + + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; + RenderSystem.disableDepthTest(); + if (this.displayRecipes) { + //Craft text - usually a requirement for the recipe + String craftText = this.recipeResults.get(this.currentPage).craftText; + if (textRenderer.getWidth(craftText) > MAX_TEXT_WIDTH) { + drawTooltip(textRenderer, context, craftText, this.parentX + 11, this.parentY + 31, mouseX, mouseY); + craftText = textRenderer.trimToWidth(craftText, MAX_TEXT_WIDTH) + ELLIPSIS; + } + context.drawTextWithShadow(textRenderer, craftText, this.parentX + 11, this.parentY + 31, 0xffffffff); + + //Item name + Text resultText = this.recipeResults.get(this.currentPage).result.getName(); + if (textRenderer.getWidth(Formatting.strip(resultText.getString())) > MAX_TEXT_WIDTH) { + drawTooltip(textRenderer, context, resultText, this.parentX + 11, this.parentY + 43, mouseX, mouseY); + resultText = Text.literal(getLegacyFormatting(resultText.getString()) + textRenderer.trimToWidth(Formatting.strip(resultText.getString()), MAX_TEXT_WIDTH) + ELLIPSIS).setStyle(resultText.getStyle()); + } + context.drawTextWithShadow(textRenderer, resultText, this.parentX + 11, this.parentY + 43, 0xffffffff); + + //Arrow pointing to result item from the recipe + context.drawTextWithShadow(textRenderer, "▶", this.parentX + 96, this.parentY + 90, 0xaaffffff); + } + for (ResultButtonWidget button : resultButtons) + button.render(context, mouseX, mouseY, delta); + if (this.pageCount > 1) { + String string = (this.currentPage + 1) + "/" + this.pageCount; + int dx = this.client.textRenderer.getWidth(string) / 2; + context.drawText(textRenderer, string, this.parentX - dx + 73, this.parentY + 141, -1, false); + } + if (this.prevPageButton.active) this.prevPageButton.render(context, mouseX, mouseY, delta); + if (this.nextPageButton.active) this.nextPageButton.render(context, mouseX, mouseY, delta); + RenderSystem.enableDepthTest(); + } + + /** + * Used for drawing tooltips over truncated text + */ + private void drawTooltip(TextRenderer textRenderer, DrawContext context, Text text, int textX, int textY, int mouseX, int mouseY){ + RenderSystem.disableDepthTest(); + if (mouseX >= textX && mouseX <= textX + MAX_TEXT_WIDTH + 4 && mouseY >= textY && mouseY <= textY + 9) { + context.drawTooltip(textRenderer, text, mouseX, mouseY); + } + RenderSystem.enableDepthTest(); + } + + /** + * @see #drawTooltip(TextRenderer, DrawContext, Text, int, int, int, int) + */ + private void drawTooltip(TextRenderer textRenderer, DrawContext context, String text, int textX, int textY, int mouseX, int mouseY){ + drawTooltip(textRenderer, context, Text.of(text), textX, textY, mouseX, mouseY); + } + + /** + * Retrieves the first occurrence of section symbol formatting in a string + * + * @param string The string to fetch section symbol formatting from + * @return The section symbol and its formatting code or {@code null} if a match isn't found or if the {@code string} is null + */ + private static String getLegacyFormatting(@Nullable String string) { + if (string == null) { + return null; + } + Matcher matcher = FORMATTING_CODE_PATTERN.matcher(string); + if (matcher.find()) { + return matcher.group(0); + } + return null; + } + + public void drawTooltip(DrawContext context, int mouseX, int mouseY) { + RenderSystem.disableDepthTest(); + for (ResultButtonWidget button : resultButtons) + if (button.isMouseOver(mouseX, mouseY)) + button.renderTooltip(context, mouseX, mouseY); + RenderSystem.enableDepthTest(); + } + + public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { + for (ResultButtonWidget button : resultButtons) + if (button.mouseClicked(mouseX, mouseY, mouseButton)) { + if (button.itemStack.getNbt() == null) { + continue; + } + String internalName = button.itemStack.getNbt().getCompound("ExtraAttributes").getString("id"); + List recipes = ItemRegistry.getRecipes(internalName); + if (!recipes.isEmpty()) { + this.recipeResults = recipes; + this.currentPage = 0; + this.pageCount = recipes.size(); + this.displayRecipes = true; + this.updateButtons(); + } + return true; + } + if (this.prevPageButton.mouseClicked(mouseX, mouseY, mouseButton)) { + --this.currentPage; + this.updateButtons(); + return true; + } + if (this.nextPageButton.mouseClicked(mouseX, mouseY, mouseButton)) { + ++this.currentPage; + this.updateButtons(); + return true; + } + return false; + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java new file mode 100644 index 00000000..b738dfef --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java @@ -0,0 +1,60 @@ +package de.hysky.skyblocker.skyblock.itemlist; + +import com.google.gson.JsonObject; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class SkyblockCraftingRecipe { + private static final Logger LOGGER = LoggerFactory.getLogger(SkyblockCraftingRecipe.class); + String craftText = ""; + final List grid = new ArrayList<>(9); + ItemStack result; + + public static SkyblockCraftingRecipe fromJsonObject(JsonObject jsonObj) { + SkyblockCraftingRecipe recipe = new SkyblockCraftingRecipe(); + if (jsonObj.has("crafttext")) recipe.craftText = jsonObj.get("crafttext").getAsString(); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A1").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A2").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A3").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B1").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B2").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B3").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C1").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C2").getAsString())); + recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C3").getAsString())); + recipe.result = ItemRegistry.itemsMap.get(jsonObj.get("internalname").getAsString()); + return recipe; + } + + private static ItemStack getItemStack(String internalName) { + try { + if (internalName.length() > 0) { + int count = internalName.split(":").length == 1 ? 1 : Integer.parseInt(internalName.split(":")[1]); + internalName = internalName.split(":")[0]; + ItemStack itemStack = ItemRegistry.itemsMap.get(internalName).copy(); + itemStack.setCount(count); + return itemStack; + } + } catch (Exception e) { + LOGGER.error("[Skyblocker-Recipe] " + internalName, e); + } + return Items.AIR.getDefaultStack(); + } + + public List getGrid() { + return grid; + } + + public ItemStack getResult() { + return result; + } + + public String getCraftText() { + return craftText; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java new file mode 100644 index 00000000..51a3d409 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNav.java @@ -0,0 +1,80 @@ +package de.hysky.skyblocker.skyblock.quicknav; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.Screens; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.PatternSyntaxException; + +public class QuickNav { + private static final String skyblockHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}"; + private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}"; + + public static void init() { + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().quickNav.enableQuickNav && screen instanceof HandledScreen && client.player != null && !client.player.isCreative()) { + String screenTitle = screen.getTitle().getString().trim(); + List buttons = QuickNav.init(screenTitle); + for (QuickNavButton button : buttons) Screens.getButtons(screen).add(button); + } + }); + } + + public static List init(String screenTitle) { + List buttons = new ArrayList<>(); + SkyblockerConfig.QuickNav data = SkyblockerConfigManager.get().quickNav; + try { + if (data.button1.render) buttons.add(parseButton(data.button1, screenTitle, 0)); + if (data.button2.render) buttons.add(parseButton(data.button2, screenTitle, 1)); + if (data.button3.render) buttons.add(parseButton(data.button3, screenTitle, 2)); + if (data.button4.render) buttons.add(parseButton(data.button4, screenTitle, 3)); + if (data.button5.render) buttons.add(parseButton(data.button5, screenTitle, 4)); + if (data.button6.render) buttons.add(parseButton(data.button6, screenTitle, 5)); + if (data.button7.render) buttons.add(parseButton(data.button7, screenTitle, 6)); + if (data.button8.render) buttons.add(parseButton(data.button8, screenTitle, 7)); + if (data.button9.render) buttons.add(parseButton(data.button9, screenTitle, 8)); + 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)); + } catch (CommandSyntaxException e) { + e.printStackTrace(); + } + return buttons; + } + + private static QuickNavButton parseButton(SkyblockerConfig.QuickNavItem buttonInfo, String screenTitle, int id) throws CommandSyntaxException { + SkyblockerConfig.ItemData itemData = buttonInfo.item; + String nbtString = "{id:\"minecraft:" + itemData.itemName.toLowerCase(Locale.ROOT) + "\",Count:1"; + if (itemData.nbt.length() > 2) nbtString += "," + itemData.nbt; + nbtString += "}"; + boolean uiTitleMatches = false; + try { + uiTitleMatches = screenTitle.matches(buttonInfo.uiTitle); + } catch (PatternSyntaxException e) { + e.printStackTrace(); + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player != null) { + player.sendMessage(Text.of(Formatting.RED + "[Skyblocker] Invalid regex in quicknav button " + (id + 1) + "!"), false); + } + } + return new QuickNavButton(id, + uiTitleMatches, + buttonInfo.clickEvent, + ItemStack.fromNbt(StringNbtReader.parse(nbtString)) + ); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java new file mode 100644 index 00000000..5e76427a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/quicknav/QuickNavButton.java @@ -0,0 +1,107 @@ +package de.hysky.skyblocker.skyblock.quicknav; + +import com.mojang.blaze3d.systems.RenderSystem; + +import de.hysky.skyblocker.mixin.accessor.HandledScreenAccessor; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +@Environment(value=EnvType.CLIENT) +public class QuickNavButton extends ClickableWidget { + private static final Identifier BUTTON_TEXTURE = new Identifier("textures/gui/container/creative_inventory/tabs.png"); + + private final int index; + private boolean toggled; + private int u; + private int v; + private final String command; + private final ItemStack icon; + + public QuickNavButton(int index, boolean toggled, String command, ItemStack icon) { + super(0, 0, 26, 32, Text.empty()); + this.index = index; + this.toggled = toggled; + this.command = command; + this.icon = icon; + } + + 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); + this.u = 26; + this.v = (index < 6 ? 0 : 64) + (toggled ? 32 : 0); + } + } + + @Override + public void onClick(double mouseX, double mouseY) { + if (!this.toggled) { + this.toggled = true; + MessageScheduler.INSTANCE.sendMessageAfterCooldown(command); + // TODO : add null check with log error + } + } + + @Override + public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) { + this.updateCoordinates(); + MatrixStack matrices = context.getMatrices(); + RenderSystem.disableDepthTest(); + // render button background + if (!this.toggled) { + if (this.index >= 6) + // this.drawTexture(matrices, this.x, this.y + 4, this.u, this.v + 4, this.width, this.height - 4); + context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY() + 4, this.u, this.v + 4, this.width, this.height - 4); + else + // this.drawTexture(matrices, this.x, this.y, this.u, this.v, this.width, this.height - 4); + context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY() - 2, this.u, this.v, this.width, this.height - 4); + // } else this.drawTexture(matrices, this.x, this.y, this.u, this.v, this.width, this.height); + } else { + matrices.push(); + //Move the top buttons 2 pixels up if they're selected + if (this.index < 6) matrices.translate(0f, -2f, 0f); + context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY(), this.u, this.v, this.width, this.height); + matrices.pop(); + } + // render button icon + if (!this.toggled) { + if (this.index >= 6) + // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 6); + context.drawItem(this.icon,this.getX() + 5, this.getY() + 6); + else + // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 9); + context.drawItem(this.icon,this.getX() + 5, this.getY() + 7); + } else { + if (this.index >= 6) + // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 9); + context.drawItem(this.icon,this.getX() + 5, this.getY() + 9); + else + // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 6); + context.drawItem(this.icon,this.getX() + 5, this.getY() + 6); + } + RenderSystem.enableDepthTest(); + } + + @Override + protected void appendClickableNarrations(NarrationMessageBuilder builder) { + // TODO Auto-generated method stub + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/EffigyWaypoints.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/EffigyWaypoints.java new file mode 100644 index 00000000..4cc20ca5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/EffigyWaypoints.java @@ -0,0 +1,71 @@ +package de.hysky.skyblocker.skyblock.rift; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.text.Text; +import net.minecraft.text.TextColor; +import net.minecraft.util.DyeColor; +import net.minecraft.util.math.BlockPos; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +public class EffigyWaypoints { + private static final Logger LOGGER = LoggerFactory.getLogger(EffigyWaypoints.class); + private static final List EFFIGIES = List.of( + new BlockPos(150, 79, 95), //Effigy 1 + new BlockPos(193, 93, 119), //Effigy 2 + new BlockPos(235, 110, 147), //Effigy 3 + new BlockPos(293, 96, 134), //Effigy 4 + new BlockPos(262, 99, 94), //Effigy 5 + new BlockPos(240, 129, 118) //Effigy 6 + ); + private static final List UNBROKEN_EFFIGIES = new ArrayList<>(); + + protected static void updateEffigies() { + if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableEffigyWaypoints || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) return; + + UNBROKEN_EFFIGIES.clear(); + + try { + for (int i = 0; i < Utils.STRING_SCOREBOARD.size(); i++) { + String line = Utils.STRING_SCOREBOARD.get(i); + + if (line.contains("Effigies")) { + List effigiesText = new ArrayList<>(); + List prefixAndSuffix = Utils.TEXT_SCOREBOARD.get(i).getSiblings(); + + //Add contents of prefix and suffix to list + effigiesText.addAll(prefixAndSuffix.get(0).getSiblings()); + effigiesText.addAll(prefixAndSuffix.get(1).getSiblings()); + + for (int i2 = 1; i2 < effigiesText.size(); i2++) { + if (effigiesText.get(i2).getStyle().getColor() == TextColor.parse("gray")) UNBROKEN_EFFIGIES.add(EFFIGIES.get(i2 - 1)); + } + } + } + } catch (NullPointerException e) { + LOGGER.error("[Skyblocker] Error while updating effigies.", e); + } + } + + protected static void render(WorldRenderContext context) { + if (SkyblockerConfigManager.get().slayer.vampireSlayer.enableEffigyWaypoints && Utils.getLocation().contains("Stillgore Château")) { + for (BlockPos effigy : UNBROKEN_EFFIGIES) { + float[] colorComponents = DyeColor.RED.getColorComponents(); + if (SkyblockerConfigManager.get().slayer.vampireSlayer.compactEffigyWaypoints) { + RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy.down(6), colorComponents, 0.5F); + } else { + RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy, colorComponents, 0.5F); + for (int i = 1; i < 6; i++) { + RenderHelper.renderFilledThroughWalls(context, effigy.down(i), colorComponents, 0.5F - (0.075F * i)); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/HealingMelonIndicator.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/HealingMelonIndicator.java new file mode 100644 index 00000000..333a4aa1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/HealingMelonIndicator.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.skyblock.rift; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.util.Formatting; + +public class HealingMelonIndicator { + private static final Title title = new Title("skyblocker.rift.healNow", Formatting.DARK_RED); + + public static void updateHealth() { + if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableHealingMelonIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) { + TitleContainer.removeTitle(title); + return; + } + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player != null && player.getHealth() <= SkyblockerConfigManager.get().slayer.vampireSlayer.healingMelonHealthThreshold * 2F) { + RenderHelper.displayInTitleContainerAndPlaySound(title); + } else { + TitleContainer.removeTitle(title); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/ManiaIndicator.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/ManiaIndicator.java new file mode 100644 index 00000000..ab252ff0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/ManiaIndicator.java @@ -0,0 +1,42 @@ +package de.hysky.skyblocker.skyblock.rift; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.SlayerUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.BlockPos; + +public class ManiaIndicator { + private static final Title title = new Title("skyblocker.rift.mania", Formatting.RED); + + protected static void updateMania() { + if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableManiaIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !(Utils.getLocation().contains("Stillgore Château")) || !SlayerUtils.isInSlayer()) { + TitleContainer.removeTitle(title); + return; + } + + Entity slayerEntity = SlayerUtils.getSlayerEntity(); + if (slayerEntity == null) return; + + boolean anyMania = false; + for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) { + if (entity.getDisplayName().toString().contains("MANIA")) { + anyMania = true; + BlockPos pos = MinecraftClient.getInstance().player.getBlockPos().down(); + boolean isGreen = MinecraftClient.getInstance().world.getBlockState(pos).getBlock() == Blocks.GREEN_TERRACOTTA; + title.setText(Text.translatable("skyblocker.rift.mania").formatted(isGreen ? Formatting.GREEN : Formatting.RED)); + RenderHelper.displayInTitleContainerAndPlaySound(title); + } + } + if (!anyMania) { + TitleContainer.removeTitle(title); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/MirrorverseWaypoints.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/MirrorverseWaypoints.java new file mode 100644 index 00000000..06181349 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/MirrorverseWaypoints.java @@ -0,0 +1,88 @@ +package de.hysky.skyblocker.skyblock.rift; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.DyeColor; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.IOException; + +public class MirrorverseWaypoints { + private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker"); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final Identifier WAYPOINTS_JSON = new Identifier(SkyblockerMod.NAMESPACE, "mirrorverse_waypoints.json"); + private static final BlockPos[] LAVA_PATH_WAYPOINTS = new BlockPos[107]; + private static final BlockPos[] UPSIDE_DOWN_WAYPOINTS = new BlockPos[66]; + private static final BlockPos[] TURBULATOR_WAYPOINTS = new BlockPos[27]; + private static final float[] COLOR_COMPONENTS = DyeColor.RED.getColorComponents(); + + static { + loadWaypoints(); + } + + /** + * Loads the waypoint locations into memory + */ + private static void loadWaypoints() { + try (BufferedReader reader = CLIENT.getResourceManager().openAsReader(WAYPOINTS_JSON)) { + JsonObject file = JsonParser.parseReader(reader).getAsJsonObject(); + JsonArray sections = file.get("sections").getAsJsonArray(); + + /// Lava Path + JsonArray lavaPathWaypoints = sections.get(0).getAsJsonObject().get("waypoints").getAsJsonArray(); + + for (int i = 0; i < lavaPathWaypoints.size(); i++) { + JsonObject point = lavaPathWaypoints.get(i).getAsJsonObject(); + LAVA_PATH_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt()); + } + + /// Upside Down Parkour + JsonArray upsideDownParkourWaypoints = sections.get(1).getAsJsonObject().get("waypoints").getAsJsonArray(); + + for (int i = 0; i < upsideDownParkourWaypoints.size(); i++) { + JsonObject point = upsideDownParkourWaypoints.get(i).getAsJsonObject(); + UPSIDE_DOWN_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt()); + } + + /// Turbulator Parkour + JsonArray turbulatorParkourWaypoints = sections.get(2).getAsJsonObject().get("waypoints").getAsJsonArray(); + + for (int i = 0; i < turbulatorParkourWaypoints.size(); i++) { + JsonObject point = turbulatorParkourWaypoints.get(i).getAsJsonObject(); + TURBULATOR_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt()); + } + + } catch (IOException e) { + LOGGER.info("[Skyblocker] Mirrorverse Waypoints failed to load ;("); + e.printStackTrace(); + } + } + + protected static void render(WorldRenderContext wrc) { + //I would also check for the mirrorverse location but the scoreboard stuff is not performant at all... + if (Utils.isInTheRift() && SkyblockerConfigManager.get().locations.rift.mirrorverseWaypoints) { + for (BlockPos pos : LAVA_PATH_WAYPOINTS) { + RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); + } + + for (BlockPos pos : UPSIDE_DOWN_WAYPOINTS) { + RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); + } + + for (BlockPos pos : TURBULATOR_WAYPOINTS) { + RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/StakeIndicator.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/StakeIndicator.java new file mode 100644 index 00000000..be502143 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/StakeIndicator.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.skyblock.rift; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.SlayerUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import net.minecraft.entity.Entity; +import net.minecraft.util.Formatting; + +public class StakeIndicator { + private static final Title title = new Title("skyblocker.rift.stakeNow", Formatting.RED); + + protected static void updateStake() { + if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableSteakStakeIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château") || !SlayerUtils.isInSlayer()) { + TitleContainer.removeTitle(title); + return; + } + Entity slayerEntity = SlayerUtils.getSlayerEntity(); + if (slayerEntity != null && slayerEntity.getDisplayName().toString().contains("҉")) { + RenderHelper.displayInTitleContainerAndPlaySound(title); + } else { + TitleContainer.removeTitle(title); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java new file mode 100644 index 00000000..10b593bd --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.skyblock.rift; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; + +public class TheRift { + /** + * @see Utils#isInTheRift() Utils#isInTheRift(). + */ + public static final String LOCATION = "rift"; + + public static void init() { + WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render); + WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render); + Scheduler.INSTANCE.scheduleCyclic(EffigyWaypoints::updateEffigies, SkyblockerConfigManager.get().slayer.vampireSlayer.effigyUpdateFrequency); + Scheduler.INSTANCE.scheduleCyclic(TwinClawsIndicator::updateIce, SkyblockerConfigManager.get().slayer.vampireSlayer.holyIceUpdateFrequency); + Scheduler.INSTANCE.scheduleCyclic(ManiaIndicator::updateMania, SkyblockerConfigManager.get().slayer.vampireSlayer.maniaUpdateFrequency); + Scheduler.INSTANCE.scheduleCyclic(StakeIndicator::updateStake, SkyblockerConfigManager.get().slayer.vampireSlayer.steakStakeUpdateFrequency); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/TwinClawsIndicator.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/TwinClawsIndicator.java new file mode 100644 index 00000000..1622bf4a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/TwinClawsIndicator.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.skyblock.rift; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.SlayerUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import net.minecraft.entity.Entity; +import net.minecraft.util.Formatting; + +public class TwinClawsIndicator { + private static final Title title = new Title("skyblocker.rift.iceNow", Formatting.AQUA); + private static boolean scheduled = false; + + protected static void updateIce() { + if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableHolyIceIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !(Utils.getLocation().contains("Stillgore Château")) || !SlayerUtils.isInSlayer()) { + TitleContainer.removeTitle(title); + return; + } + + Entity slayerEntity = SlayerUtils.getSlayerEntity(); + if (slayerEntity == null) return; + + boolean anyClaws = false; + for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) { + if (entity.getDisplayName().toString().contains("TWINCLAWS")) { + anyClaws = true; + if (!TitleContainer.containsTitle(title) && !scheduled) { + scheduled = true; + Scheduler.INSTANCE.schedule(() -> { + RenderHelper.displayInTitleContainerAndPlaySound(title); + scheduled = false; + }, SkyblockerConfigManager.get().slayer.vampireSlayer.holyIceIndicatorTickDelay); + } + } + } + if (!anyClaws) { + TitleContainer.removeTitle(title); + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/shortcut/Shortcuts.java b/src/main/java/de/hysky/skyblocker/skyblock/shortcut/Shortcuts.java new file mode 100644 index 00000000..9c058a4f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/shortcut/Shortcuts.java @@ -0,0 +1,208 @@ +package de.hysky.skyblocker.skyblock.shortcut; + +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +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.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class Shortcuts { + private static final Logger LOGGER = LoggerFactory.getLogger(Shortcuts.class); + private static final File SHORTCUTS_FILE = SkyblockerMod.CONFIG_DIR.resolve("shortcuts.json").toFile(); + @Nullable + private static CompletableFuture shortcutsLoaded; + public static final Map commands = new HashMap<>(); + public static final Map commandArgs = new HashMap<>(); + + public static boolean isShortcutsLoaded() { + return shortcutsLoaded != null && shortcutsLoaded.isDone(); + } + + public static void init() { + loadShortcuts(); + ClientLifecycleEvents.CLIENT_STOPPING.register(Shortcuts::saveShortcuts); + ClientCommandRegistrationCallback.EVENT.register(Shortcuts::registerCommands); + ClientSendMessageEvents.MODIFY_COMMAND.register(Shortcuts::modifyCommand); + } + + protected static void loadShortcuts() { + if (shortcutsLoaded != null && !isShortcutsLoaded()) { + return; + } + shortcutsLoaded = CompletableFuture.runAsync(() -> { + try (BufferedReader reader = new BufferedReader(new FileReader(SHORTCUTS_FILE))) { + Type shortcutsType = new TypeToken>>() { + }.getType(); + Map> shortcuts = SkyblockerMod.GSON.fromJson(reader, shortcutsType); + commands.clear(); + commandArgs.clear(); + commands.putAll(shortcuts.get("commands")); + commandArgs.putAll(shortcuts.get("commandArgs")); + LOGGER.info("[Skyblocker] Loaded {} command shortcuts and {} command argument shortcuts", commands.size(), commandArgs.size()); + } catch (FileNotFoundException e) { + registerDefaultShortcuts(); + LOGGER.warn("[Skyblocker] Shortcuts file not found, using default shortcuts. This is normal when using for the first time."); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to load shortcuts file", e); + } + }); + } + + private static void registerDefaultShortcuts() { + commands.clear(); + commandArgs.clear(); + + // Skyblock + commands.put("/s", "/skyblock"); + commands.put("/i", "/is"); + commands.put("/h", "/hub"); + + // Dungeon + commands.put("/d", "/warp dungeon_hub"); + + // Chat channels + commands.put("/ca", "/chat all"); + commands.put("/cp", "/chat party"); + commands.put("/cg", "/chat guild"); + commands.put("/co", "/chat officer"); + commands.put("/cc", "/chat coop"); + + // Message + commandArgs.put("/m", "/msg"); + + // Party + commandArgs.put("/pa", "/p accept"); + commands.put("/pv", "/p leave"); + commands.put("/pd", "/p disband"); + commands.put("/rp", "/reparty"); + + // Visit + commandArgs.put("/v", "/visit"); + commands.put("/vp", "/visit portalhub"); + } + + @SuppressWarnings("unused") + private static void registerMoreDefaultShortcuts() { + // Combat + commands.put("/spider", "/warp spider"); + commands.put("/crimson", "/warp nether"); + commands.put("/end", "/warp end"); + + // Mining + commands.put("/gold", "/warp gold"); + commands.put("/cavern", "/warp deep"); + commands.put("/dwarven", "/warp mines"); + commands.put("/fo", "/warp forge"); + commands.put("/ch", "/warp crystals"); + + // Foraging & Farming + commands.put("/park", "/warp park"); + commands.put("/barn", "/warp barn"); + commands.put("/desert", "/warp desert"); + commands.put("/ga", "/warp garden"); + + // Other warps + commands.put("/castle", "/warp castle"); + commands.put("/museum", "/warp museum"); + commands.put("/da", "/warp da"); + commands.put("/crypt", "/warp crypt"); + commands.put("/nest", "/warp nest"); + commands.put("/magma", "/warp magma"); + commands.put("/void", "/warp void"); + commands.put("/drag", "/warp drag"); + commands.put("/jungle", "/warp jungle"); + commands.put("/howl", "/warp howl"); + } + + protected static void saveShortcuts(MinecraftClient client) { + JsonObject shortcutsJson = new JsonObject(); + shortcutsJson.add("commands", SkyblockerMod.GSON.toJsonTree(commands)); + shortcutsJson.add("commandArgs", SkyblockerMod.GSON.toJsonTree(commandArgs)); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(SHORTCUTS_FILE))) { + SkyblockerMod.GSON.toJson(shortcutsJson, writer); + LOGGER.info("[Skyblocker] Saved {} command shortcuts and {} command argument shortcuts", commands.size(), commandArgs.size()); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to save shortcuts file", e); + } + } + + private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + for (String key : commands.keySet()) { + if (key.startsWith("/")) { + dispatcher.register(literal(key.substring(1))); + } + } + for (String key : commandArgs.keySet()) { + if (key.startsWith("/")) { + dispatcher.register(literal(key.substring(1)).then(argument("args", StringArgumentType.greedyString()))); + } + } + dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("help").executes(context -> { + FabricClientCommandSource source = context.getSource(); + String status = SkyblockerConfigManager.get().general.shortcuts.enableShortcuts && SkyblockerConfigManager.get().general.shortcuts.enableCommandShortcuts ? "§a§l (Enabled)" : "§c§l (Disabled)"; + source.sendFeedback(Text.of("§e§lSkyblocker §fCommand Shortcuts" + status)); + if (!isShortcutsLoaded()) { + source.sendFeedback(Text.translatable("skyblocker.shortcuts.notLoaded")); + } else for (Map.Entry command : commands.entrySet()) { + source.sendFeedback(Text.of("§7" + command.getKey() + " §f→ §7" + command.getValue())); + } + status = SkyblockerConfigManager.get().general.shortcuts.enableShortcuts && SkyblockerConfigManager.get().general.shortcuts.enableCommandArgShortcuts ? "§a§l (Enabled)" : "§c§l (Disabled)"; + source.sendFeedback(Text.of("§e§lSkyblocker §fCommand Argument Shortcuts" + status)); + if (!isShortcutsLoaded()) { + source.sendFeedback(Text.translatable("skyblocker.shortcuts.notLoaded")); + } else for (Map.Entry commandArg : commandArgs.entrySet()) { + source.sendFeedback(Text.of("§7" + commandArg.getKey() + " §f→ §7" + commandArg.getValue())); + } + source.sendFeedback(Text.of("§e§lSkyblocker §fCommands")); + for (String command : dispatcher.getSmartUsage(dispatcher.getRoot().getChild(SkyblockerMod.NAMESPACE), source).values()) { + source.sendFeedback(Text.of("§7/" + SkyblockerMod.NAMESPACE + " " + command)); + } + return Command.SINGLE_SUCCESS; + // Queue the screen or else the screen will be immediately closed after executing this command + })).then(literal("shortcuts").executes(Scheduler.queueOpenScreenCommand(ShortcutsConfigScreen::new)))); + } + + private static String modifyCommand(String command) { + if (SkyblockerConfigManager.get().general.shortcuts.enableShortcuts) { + if (!isShortcutsLoaded()) { + LOGGER.warn("[Skyblocker] Shortcuts not loaded yet, skipping shortcut for command: {}", command); + return command; + } + command = '/' + command; + if (SkyblockerConfigManager.get().general.shortcuts.enableCommandShortcuts) { + command = commands.getOrDefault(command, command); + } + if (SkyblockerConfigManager.get().general.shortcuts.enableCommandArgShortcuts) { + String[] messageArgs = command.split(" "); + for (int i = 0; i < messageArgs.length; i++) { + messageArgs[i] = commandArgs.getOrDefault(messageArgs[i], messageArgs[i]); + } + command = String.join(" ", messageArgs); + } + return command.substring(1); + } + return command; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java new file mode 100644 index 00000000..5ebe4c1a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java @@ -0,0 +1,232 @@ +package de.hysky.skyblocker.skyblock.shortcut; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.Selectable; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.screen.narration.NarrationPart; +import net.minecraft.client.gui.widget.ElementListWidget; +import net.minecraft.client.gui.widget.TextFieldWidget; +import net.minecraft.text.Text; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.stream.Stream; + +public class ShortcutsConfigListWidget extends ElementListWidget { + private final ShortcutsConfigScreen screen; + private final List> shortcutMaps = new ArrayList<>(); + + public ShortcutsConfigListWidget(MinecraftClient minecraftClient, ShortcutsConfigScreen screen, int width, int height, int top, int bottom, int itemHeight) { + super(minecraftClient, width, height, top, bottom, itemHeight); + this.screen = screen; + ShortcutCategoryEntry commandCategory = new ShortcutCategoryEntry(Shortcuts.commands, "skyblocker.shortcuts.command.target", "skyblocker.shortcuts.command.replacement"); + if (Shortcuts.isShortcutsLoaded()) { + commandCategory.shortcutsMap.keySet().stream().sorted().forEach(commandTarget -> addEntry(new ShortcutEntry(commandCategory, commandTarget))); + } else { + addEntry(new ShortcutLoadingEntry()); + } + ShortcutCategoryEntry commandArgCategory = new ShortcutCategoryEntry(Shortcuts.commandArgs, "skyblocker.shortcuts.commandArg.target", "skyblocker.shortcuts.commandArg.replacement", "skyblocker.shortcuts.commandArg.tooltip"); + if (Shortcuts.isShortcutsLoaded()) { + commandArgCategory.shortcutsMap.keySet().stream().sorted().forEach(commandArgTarget -> addEntry(new ShortcutEntry(commandArgCategory, commandArgTarget))); + } else { + addEntry(new ShortcutLoadingEntry()); + } + } + + @Override + public int getRowWidth() { + return super.getRowWidth() + 100; + } + + @Override + protected int getScrollbarPositionX() { + return super.getScrollbarPositionX() + 50; + } + + protected Optional getCategory() { + if (getSelectedOrNull() instanceof ShortcutCategoryEntry category) { + return Optional.of(category); + } else if (getSelectedOrNull() instanceof ShortcutEntry shortcutEntry) { + return Optional.of(shortcutEntry.category); + } + return Optional.empty(); + } + + @Override + public void setSelected(@Nullable ShortcutsConfigListWidget.AbstractShortcutEntry entry) { + super.setSelected(entry); + screen.updateButtons(); + } + + protected void addShortcutAfterSelected() { + getCategory().ifPresent(category -> children().add(children().indexOf(getSelectedOrNull()) + 1, new ShortcutEntry(category))); + } + + @Override + protected boolean removeEntry(AbstractShortcutEntry entry) { + return super.removeEntry(entry); + } + + protected boolean hasChanges() { + ShortcutEntry[] notEmptyShortcuts = getNotEmptyShortcuts().toArray(ShortcutEntry[]::new); + return notEmptyShortcuts.length != shortcutMaps.stream().mapToInt(Map::size).sum() || Arrays.stream(notEmptyShortcuts).anyMatch(ShortcutEntry::isChanged); + } + + protected void saveShortcuts() { + shortcutMaps.forEach(Map::clear); + getNotEmptyShortcuts().forEach(ShortcutEntry::save); + Shortcuts.saveShortcuts(MinecraftClient.getInstance()); // Save shortcuts to disk + } + + private Stream getNotEmptyShortcuts() { + return children().stream().filter(ShortcutEntry.class::isInstance).map(ShortcutEntry.class::cast).filter(ShortcutEntry::isNotEmpty); + } + + protected static abstract class AbstractShortcutEntry extends ElementListWidget.Entry { + } + + private class ShortcutCategoryEntry extends AbstractShortcutEntry { + private final Map shortcutsMap; + private final Text targetName; + private final Text replacementName; + @Nullable + private final Text tooltip; + + private ShortcutCategoryEntry(Map shortcutsMap, String targetName, String replacementName) { + this(shortcutsMap, targetName, replacementName, (Text) null); + } + + private ShortcutCategoryEntry(Map shortcutsMap, String targetName, String replacementName, String tooltip) { + this(shortcutsMap, targetName, replacementName, Text.translatable(tooltip)); + } + + private ShortcutCategoryEntry(Map shortcutsMap, String targetName, String replacementName, @Nullable Text tooltip) { + this.shortcutsMap = shortcutsMap; + this.targetName = Text.translatable(targetName); + this.replacementName = Text.translatable(replacementName); + this.tooltip = tooltip; + shortcutMaps.add(shortcutsMap); + addEntry(this); + } + + @Override + public List children() { + return List.of(); + } + + @Override + public List selectableChildren() { + return List.of(new Selectable() { + @Override + public SelectionType getType() { + return SelectionType.HOVERED; + } + + @Override + public void appendNarrations(NarrationMessageBuilder builder) { + builder.put(NarrationPart.TITLE, targetName, replacementName); + } + }); + } + + @Override + public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + context.drawCenteredTextWithShadow(client.textRenderer, targetName, width / 2 - 85, y + 5, 0xFFFFFF); + context.drawCenteredTextWithShadow(client.textRenderer, replacementName, width / 2 + 85, y + 5, 0xFFFFFF); + if (tooltip != null && isMouseOver(mouseX, mouseY)) { + screen.setTooltip(tooltip); + } + } + } + + private class ShortcutLoadingEntry extends AbstractShortcutEntry { + private final Text text; + + private ShortcutLoadingEntry() { + this.text = Text.translatable("skyblocker.shortcuts.notLoaded"); + } + + @Override + public List children() { + return List.of(); + } + + @Override + public List selectableChildren() { + return List.of(new Selectable() { + @Override + public SelectionType getType() { + return SelectionType.HOVERED; + } + + @Override + public void appendNarrations(NarrationMessageBuilder builder) { + builder.put(NarrationPart.TITLE, text); + } + }); + } + + @Override + public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + context.drawCenteredTextWithShadow(client.textRenderer, text, width / 2, y + 5, 0xFFFFFF); + } + } + + protected class ShortcutEntry extends AbstractShortcutEntry { + private final List children; + private final ShortcutCategoryEntry category; + private final TextFieldWidget target; + private final TextFieldWidget replacement; + + private ShortcutEntry(ShortcutCategoryEntry category) { + this(category, ""); + } + + private ShortcutEntry(ShortcutCategoryEntry category, String targetString) { + this.category = category; + target = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, width / 2 - 160, 5, 150, 20, category.targetName); + replacement = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, width / 2 + 10, 5, 150, 20, category.replacementName); + target.setText(targetString); + replacement.setText(category.shortcutsMap.getOrDefault(targetString, "")); + children = List.of(target, replacement); + } + + @Override + public String toString() { + return target.getText() + " → " + replacement.getText(); + } + + @Override + public List children() { + return children; + } + + @Override + public List selectableChildren() { + return children; + } + + private boolean isNotEmpty() { + return !target.getText().isEmpty() && !replacement.getText().isEmpty(); + } + + private boolean isChanged() { + return !category.shortcutsMap.containsKey(target.getText()) || !category.shortcutsMap.get(target.getText()).equals(replacement.getText()); + } + + private void save() { + category.shortcutsMap.put(target.getText(), replacement.getText()); + } + + @Override + public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { + target.setY(y); + replacement.setY(y); + target.render(context, mouseX, mouseY, tickDelta); + replacement.render(context, mouseX, mouseY, tickDelta); + context.drawCenteredTextWithShadow(client.textRenderer, "→", width / 2, y + 5, 0xFFFFFF); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java new file mode 100644 index 00000000..196ad0d6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java @@ -0,0 +1,113 @@ +package de.hysky.skyblocker.skyblock.shortcut; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ConfirmScreen; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tooltip.Tooltip; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.GridWidget; +import net.minecraft.client.gui.widget.SimplePositioningWidget; +import net.minecraft.screen.ScreenTexts; +import net.minecraft.text.Text; + +public class ShortcutsConfigScreen extends Screen { + + private ShortcutsConfigListWidget shortcutsConfigListWidget; + private ButtonWidget buttonDelete; + private ButtonWidget buttonNew; + private ButtonWidget buttonDone; + private boolean initialized; + private double scrollAmount; + private final Screen parent; + + public ShortcutsConfigScreen() { + this(null); + } + + public ShortcutsConfigScreen(Screen parent) { + super(Text.translatable("skyblocker.shortcuts.config")); + this.parent = parent; + } + + @Override + public void setTooltip(Text tooltip) { + super.setTooltip(tooltip); + } + + @Override + protected void init() { + super.init(); + if (initialized) { + shortcutsConfigListWidget.updateSize(width, height, 32, height - 64); + } else { + shortcutsConfigListWidget = new ShortcutsConfigListWidget(client, this, width, height, 32, height - 64, 25); + initialized = true; + } + addDrawableChild(shortcutsConfigListWidget); + GridWidget gridWidget = new GridWidget(); + gridWidget.getMainPositioner().marginX(5).marginY(2); + GridWidget.Adder adder = gridWidget.createAdder(2); + buttonDelete = ButtonWidget.builder(Text.translatable("selectServer.delete"), button -> { + if (client != null && shortcutsConfigListWidget.getSelectedOrNull() instanceof ShortcutsConfigListWidget.ShortcutEntry shortcutEntry) { + scrollAmount = shortcutsConfigListWidget.getScrollAmount(); + client.setScreen(new ConfirmScreen(this::deleteEntry, Text.translatable("skyblocker.shortcuts.deleteQuestion"), Text.translatable("skyblocker.shortcuts.deleteWarning", shortcutEntry), Text.translatable("selectServer.deleteButton"), ScreenTexts.CANCEL)); + } + }).build(); + adder.add(buttonDelete); + buttonNew = ButtonWidget.builder(Text.translatable("skyblocker.shortcuts.new"), buttonNew -> shortcutsConfigListWidget.addShortcutAfterSelected()).build(); + adder.add(buttonNew); + adder.add(ButtonWidget.builder(ScreenTexts.CANCEL, button -> { + if (client != null) { + close(); + } + }).build()); + buttonDone = ButtonWidget.builder(ScreenTexts.DONE, button -> { + shortcutsConfigListWidget.saveShortcuts(); + if (client != null) { + close(); + } + }).tooltip(Tooltip.of(Text.translatable("skyblocker.shortcuts.commandSuggestionTooltip"))).build(); + adder.add(buttonDone); + gridWidget.refreshPositions(); + SimplePositioningWidget.setPos(gridWidget, 0, this.height - 64, this.width, 64); + gridWidget.forEachChild(this::addDrawableChild); + updateButtons(); + } + + private void deleteEntry(boolean confirmedAction) { + if (client != null) { + if (confirmedAction && shortcutsConfigListWidget.getSelectedOrNull() instanceof ShortcutsConfigListWidget.ShortcutEntry shortcutEntry) { + shortcutsConfigListWidget.removeEntry(shortcutEntry); + } + client.setScreen(this); // Re-inits the screen and keeps the old instance of ShortcutsConfigListWidget + shortcutsConfigListWidget.setScrollAmount(scrollAmount); + } + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 16, 0xFFFFFF); + } + + @Override + public void close() { + if (client != null && shortcutsConfigListWidget.hasChanges()) { + client.setScreen(new ConfirmScreen(confirmedAction -> { + if (confirmedAction) { + this.client.setScreen(parent); + } else { + client.setScreen(this); + } + }, Text.translatable("text.skyblocker.quit_config"), Text.translatable("text.skyblocker.quit_config_sure"), Text.translatable("text.skyblocker.quit_discard"), ScreenTexts.CANCEL)); + } else { + this.client.setScreen(parent); + } + } + + protected void updateButtons() { + buttonDelete.active = Shortcuts.isShortcutsLoaded() && shortcutsConfigListWidget.getSelectedOrNull() instanceof ShortcutsConfigListWidget.ShortcutEntry; + buttonNew.active = Shortcuts.isShortcutsLoaded() && shortcutsConfigListWidget.getCategory().isPresent(); + buttonDone.active = Shortcuts.isShortcutsLoaded(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java b/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java new file mode 100644 index 00000000..fba447ea --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/special/SpecialEffects.java @@ -0,0 +1,96 @@ +package de.hysky.skyblocker.skyblock.special; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.enchantment.Enchantments; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.particle.ParticleTypes; +import net.minecraft.text.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SpecialEffects { + private static final Logger LOGGER = LoggerFactory.getLogger(SpecialEffects.class); + private static final Pattern DROP_PATTERN = Pattern.compile("(?:\\[[A-Z+]+] )?(?[A-Za-z0-9_]+) unlocked (?.+)!"); + private static final ItemStack NECRON_HANDLE = new ItemStack(Items.STICK); + private static final ItemStack SCROLL = new ItemStack(Items.WRITABLE_BOOK); + private static ItemStack TIER_5_SKULL; + private static ItemStack FIFTH_STAR; + + static { + NECRON_HANDLE.addEnchantment(Enchantments.PROTECTION, 1); + SCROLL.addEnchantment(Enchantments.PROTECTION, 1); + try { + TIER_5_SKULL = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-1613868903,-527154034,-1445577520,748807544],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTEwZjlmMTA4NWQ0MDcxNDFlYjc3NjE3YTRhYmRhYWEwOGQ4YWYzM2I5NjAyMDBmZThjMTI2YzFkMTQ0NTY4MiJ9fX0=\"}]}}}}")); + FIFTH_STAR = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1904417095,756174249,-1302927470,1407004198],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzFjODA0MjUyN2Y4MWM4ZTI5M2UyODEwMTEzNDg5ZjQzOTRjYzZlZmUxNWQxYWZhYzQzMTU3MWM3M2I2MmRjNCJ9fX0=\"}]}}}}")); + } catch (Exception e) { + TIER_5_SKULL = ItemStack.EMPTY; + FIFTH_STAR = ItemStack.EMPTY; + LOGGER.error("[Skyblocker Special Effects] Failed to parse NBT for a player head!", e); + } + } + + public static void init() { + ClientReceiveMessageEvents.GAME.register(SpecialEffects::displayRareDropEffect); + } + + private static void displayRareDropEffect(Text message, boolean overlay) { + //We don't check if we're in dungeons because that check doesn't work in m7 which defeats the point of this + //It might also allow it to work with Croesus + if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.specialEffects.rareDungeonDropEffects) { + try { + String stringForm = message.getString(); + Matcher matcher = DROP_PATTERN.matcher(stringForm); + + if (matcher.matches()) { + MinecraftClient client = MinecraftClient.getInstance(); + String player = matcher.group("player"); + + if (player.equals(client.getSession().getUsername())) { + ItemStack stack = getStackFromName(matcher.group("item")); + + if (!stack.isEmpty()) { + if (RenderSystem.isOnRenderThread()) { + client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30); + client.gameRenderer.showFloatingItem(stack); + } else { + RenderSystem.recordRenderCall(() -> { + client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30); + client.gameRenderer.showFloatingItem(stack); + }); + } + } + } + } + } catch (Exception e) { //In case there's a regex failure or something else bad happens + LOGGER.error("[Skyblocker Special Effects] An unexpected exception was encountered: ", e); + } + } + } + + private static ItemStack getStackFromName(String itemName) { + return switch (itemName) { + //M7 + case "Necron Dye" -> new ItemStack(Items.ORANGE_DYE); + case "Dark Claymore" -> new ItemStack(Items.STONE_SWORD); + case "Necron's Handle", "Shiny Necron's Handle" -> NECRON_HANDLE; + case "Enchanted Book (Thunderlord VII)" -> new ItemStack(Items.ENCHANTED_BOOK); + case "Master Skull - Tier 5" -> TIER_5_SKULL; + case "Shadow Warp", "Wither Shield", "Implosion" -> SCROLL; + case "Fifth Master Star" -> FIFTH_STAR; + + //M6 + case "Giant's Sword" -> new ItemStack(Items.IRON_SWORD); + + default -> ItemStack.EMPTY; + }; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/spidersden/Relics.java b/src/main/java/de/hysky/skyblocker/skyblock/spidersden/Relics.java new file mode 100644 index 00000000..e5223874 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/spidersden/Relics.java @@ -0,0 +1,171 @@ +package de.hysky.skyblocker.skyblock.spidersden; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.mojang.brigadier.CommandDispatcher; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.PosUtils; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.DyeColor; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; + +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + +public class Relics { + private static final Logger LOGGER = LoggerFactory.getLogger(Relics.class); + private static CompletableFuture relicsLoaded; + @SuppressWarnings({"unused", "FieldCanBeLocal"}) + private static int totalRelics = 0; + private static final List relics = new ArrayList<>(); + private static final Map> foundRelics = new HashMap<>(); + + public static void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(Relics::loadRelics); + ClientLifecycleEvents.CLIENT_STOPPING.register(Relics::saveFoundRelics); + ClientCommandRegistrationCallback.EVENT.register(Relics::registerCommands); + WorldRenderEvents.AFTER_TRANSLUCENT.register(Relics::render); + ClientReceiveMessageEvents.GAME.register(Relics::onChatMessage); + } + + private static void loadRelics(MinecraftClient client) { + relicsLoaded = CompletableFuture.runAsync(() -> { + try (BufferedReader reader = client.getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "spidersden/relics.json"))) { + for (Map.Entry json : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { + if (json.getKey().equals("total")) { + totalRelics = json.getValue().getAsInt(); + } else if (json.getKey().equals("locations")) { + for (JsonElement locationJson : json.getValue().getAsJsonArray().asList()) { + JsonObject posData = locationJson.getAsJsonObject(); + relics.add(new BlockPos(posData.get("x").getAsInt(), posData.get("y").getAsInt(), posData.get("z").getAsInt())); + } + } + } + LOGGER.info("[Skyblocker] Loaded relics locations"); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to load relics locations", e); + } + + try (BufferedReader reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_relics.json").toFile()))) { + for (Map.Entry profileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { + Set foundRelicsForProfile = new HashSet<>(); + for (JsonElement foundRelicsJson : profileJson.getValue().getAsJsonArray().asList()) { + foundRelicsForProfile.add(PosUtils.parsePosString(foundRelicsJson.getAsString())); + } + foundRelics.put(profileJson.getKey(), foundRelicsForProfile); + } + LOGGER.debug("[Skyblocker] Loaded found relics"); + } catch (FileNotFoundException ignored) { + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to load found relics", e); + } + }); + } + + private static void saveFoundRelics(MinecraftClient client) { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_relics.json").toFile()))) { + JsonObject json = new JsonObject(); + for (Map.Entry> foundRelicsForProfile : foundRelics.entrySet()) { + JsonArray foundRelicsJson = new JsonArray(); + for (BlockPos foundRelic : foundRelicsForProfile.getValue()) { + foundRelicsJson.add(PosUtils.getPosString(foundRelic)); + } + json.add(foundRelicsForProfile.getKey(), foundRelicsJson); + } + SkyblockerMod.GSON.toJson(json, writer); + LOGGER.debug("[Skyblocker] Saved found relics"); + } catch (IOException e) { + LOGGER.error("[Skyblocker] Failed to write found relics to file", e); + } + } + + private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(literal(SkyblockerMod.NAMESPACE) + .then(literal("relics") + .then(literal("markAllFound").executes(context -> { + Relics.markAllFound(); + context.getSource().sendFeedback(Text.translatable("skyblocker.relics.markAllFound")); + return 1; + })) + .then(literal("markAllMissing").executes(context -> { + Relics.markAllMissing(); + context.getSource().sendFeedback(Text.translatable("skyblocker.relics.markAllMissing")); + return 1; + })))); + } + + private static void render(WorldRenderContext context) { + SkyblockerConfig.Relics config = SkyblockerConfigManager.get().locations.spidersDen.relics; + + if (config.enableRelicsHelper && relicsLoaded.isDone() && Utils.getLocationRaw().equals("combat_1")) { + for (BlockPos fairySoulPos : relics) { + boolean isRelicMissing = isRelicMissing(fairySoulPos); + if (!isRelicMissing && !config.highlightFoundRelics) continue; + float[] colorComponents = isRelicMissing ? DyeColor.YELLOW.getColorComponents() : DyeColor.BROWN.getColorComponents(); + RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoulPos, colorComponents, 0.5F); + } + } + } + + private static void onChatMessage(Text text, boolean overlay) { + String message = text.getString(); + if (message.equals("You've already found this relic!") || message.startsWith("+10,000 Coins! (") && message.endsWith("/28 Relics)")) { + markClosestRelicFound(); + } + } + + private static void markClosestRelicFound() { + if (!relicsLoaded.isDone()) return; + PlayerEntity player = MinecraftClient.getInstance().player; + if (player == null) { + LOGGER.warn("[Skyblocker] Failed to mark closest relic as found because player is null"); + return; + } + relics.stream() + .filter(Relics::isRelicMissing) + .min(Comparator.comparingDouble(relicPos -> relicPos.getSquaredDistance(player.getPos()))) + .filter(relicPos -> relicPos.getSquaredDistance(player.getPos()) <= 16) + .ifPresent(relicPos -> { + foundRelics.computeIfAbsent(Utils.getProfile(), profileKey -> new HashSet<>()); + foundRelics.get(Utils.getProfile()).add(relicPos); + }); + } + + private static boolean isRelicMissing(BlockPos relicPos) { + Set foundRelicsForProfile = foundRelics.get(Utils.getProfile()); + return foundRelicsForProfile == null || !foundRelicsForProfile.contains(relicPos); + } + + private static void markAllFound() { + foundRelics.computeIfAbsent(Utils.getProfile(), profileKey -> new HashSet<>()); + foundRelics.get(Utils.getProfile()).addAll(relics); + } + + private static void markAllMissing() { + Set foundRelicsForProfile = foundRelics.get(Utils.getProfile()); + if (foundRelicsForProfile != null) { + foundRelicsForProfile.clear(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java new file mode 100644 index 00000000..f226f371 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/TabHud.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.tabhud; + +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.minecraft.client.option.KeyBinding; +import net.minecraft.client.util.InputUtil; + +public class TabHud { + + public static KeyBinding toggleB; + public static KeyBinding toggleA; + // public static KeyBinding mapTgl; + public static KeyBinding defaultTgl; + + public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Tab HUD"); + + public static void init() { + + toggleB = KeyBindingHelper.registerKeyBinding( + new KeyBinding("key.skyblocker.toggleB", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_B, + "key.categories.skyblocker")); + toggleA = KeyBindingHelper.registerKeyBinding( + new KeyBinding("key.skyblocker.toggleA", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_N, + "key.categories.skyblocker")); + defaultTgl = KeyBindingHelper.registerKeyBinding( + new KeyBinding("key.skyblocker.defaultTgl", + InputUtil.Type.KEYSYM, + GLFW.GLFW_KEY_M, + "key.categories.skyblocker")); + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java new file mode 100644 index 00000000..ceeaa365 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java @@ -0,0 +1,179 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder; + +import java.io.BufferedReader; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.NoSuchElementException; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.AlignStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.CollideStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PipelineStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PlaceStage; +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline.StackStage; +import de.hysky.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget; +import de.hysky.skyblocker.skyblock.tabhud.widget.ErrorWidget; +import de.hysky.skyblocker.skyblock.tabhud.widget.EventWidget; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.util.Identifier; + +public class ScreenBuilder { + + // layout pipeline + private final ArrayList layoutPipeline = new ArrayList<>(); + + // all widget instances this builder knows + private final ArrayList instances = new ArrayList<>(); + // maps alias -> widget instance + private final HashMap objectMap = new HashMap<>(); + + private final String builderName; + + /** + * Create a ScreenBuilder from a json. + */ + public ScreenBuilder(Identifier ident) { + + try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager().openAsReader(ident)) { + this.builderName = ident.getPath(); + + JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); + + JsonArray widgets = json.getAsJsonArray("widgets"); + JsonArray layout = json.getAsJsonArray("layout"); + + for (JsonElement w : widgets) { + JsonObject widget = w.getAsJsonObject(); + String name = widget.get("name").getAsString(); + String alias = widget.get("alias").getAsString(); + + Widget wid = instanceFrom(name, widget); + objectMap.put(alias, wid); + instances.add(wid); + } + + for (JsonElement l : layout) { + PipelineStage ps = createStage(l.getAsJsonObject()); + layoutPipeline.add(ps); + } + } catch (Exception ex) { + // rethrow as unchecked exception so that I don't have to catch anything in the ScreenMaster + throw new IllegalStateException("Failed to load file " + ident + ". Reason: " + ex.getMessage()); + } + } + + /** + * Try to find a class in the widget package that has the supplied name and + * call it's constructor. Manual work is required if the class has arguments. + */ + public Widget instanceFrom(String name, JsonObject widget) { + + // do widgets that require args the normal way + JsonElement arg; + switch (name) { + case "EventWidget" -> { + return new EventWidget(widget.get("inGarden").getAsBoolean()); + } + case "DungeonPlayerWidget" -> { + return new DungeonPlayerWidget(widget.get("player").getAsInt()); + } + case "ErrorWidget" -> { + arg = widget.get("text"); + if (arg == null) { + return new ErrorWidget(); + } else { + return new ErrorWidget(arg.getAsString()); + } + } + case "Widget" -> + // clown case sanity check. don't instantiate the superclass >:| + throw new NoSuchElementException(builderName + "[ERROR]: No such Widget type \"Widget\"!"); + } + + // reflect something together for the "normal" ones. + + // list all packages that might contain widget classes + // using Package isn't reliable, as some classes might not be loaded yet, + // causing the packages not to show. + String packbase = "de.hysky.skyblocker.skyblock.tabhud.widget"; + String[] packnames = { + packbase, + packbase + ".rift" + }; + + // construct the full class name and try to load. + Class clazz = null; + for (String pn : packnames) { + try { + clazz = Class.forName(pn + "." + name); + } catch (LinkageError | ClassNotFoundException ex) { + continue; + } + } + + // load failed. + if (clazz == null) { + throw new NoSuchElementException(builderName + "/[ERROR]: No such Widget type \"" + name + "\"!"); + } + + // return instance of that class. + try { + Constructor ctor = clazz.getConstructor(); + return (Widget) ctor.newInstance(); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException + | IllegalArgumentException | InvocationTargetException | SecurityException ex) { + throw new IllegalStateException(builderName + "/" + name + ": Internal error..."); + } + } + + /** + * Create a PipelineStage from a json object. + */ + public PipelineStage createStage(JsonObject descr) throws NoSuchElementException { + + String op = descr.get("op").getAsString(); + + return switch (op) { + case "place" -> new PlaceStage(this, descr); + case "stack" -> new StackStage(this, descr); + case "align" -> new AlignStage(this, descr); + case "collideAgainst" -> new CollideStage(this, descr); + default -> throw new NoSuchElementException("No such op " + op + " as requested by " + this.builderName); + }; + } + + /** + * Lookup Widget instance from alias name + */ + public Widget getInstance(String name) { + if (!this.objectMap.containsKey(name)) { + throw new NoSuchElementException("No widget with alias " + name + " in screen " + builderName); + } + return this.objectMap.get(name); + } + + /** + * Run the pipeline to build a Screen + */ + public void run(DrawContext context, int screenW, int screenH) { + + for (Widget w : instances) { + w.update(); + } + for (PipelineStage ps : layoutPipeline) { + ps.run(screenW, screenH); + } + for (Widget w : instances) { + w.render(context); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java new file mode 100644 index 00000000..210d8001 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java @@ -0,0 +1,144 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder; + +import java.io.BufferedReader; +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import de.hysky.skyblocker.skyblock.tabhud.TabHud; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerLocator; +import net.fabricmc.fabric.api.resource.ResourceManagerHelper; +import net.fabricmc.fabric.api.resource.ResourcePackActivationType; +import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.resource.ResourceType; +import net.minecraft.util.Identifier; + +public class ScreenMaster { + + private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker"); + + private static final int VERSION = 1; + + private static final HashMap standardMap = new HashMap<>(); + private static final HashMap screenAMap = new HashMap<>(); + private static final HashMap screenBMap = new HashMap<>(); + + /** + * Load a screen mapping from an identifier + */ + public static void load(Identifier ident) { + + String path = ident.getPath(); + String[] parts = path.split("/"); + String screenType = parts[parts.length - 2]; + String location = parts[parts.length - 1]; + location = location.replace(".json", ""); + + ScreenBuilder sb = new ScreenBuilder(ident); + switch (screenType) { + case "standard" -> standardMap.put(location, sb); + case "screen_a" -> screenAMap.put(location, sb); + case "screen_b" -> screenBMap.put(location, sb); + } + } + + /** + * Top level render method. + * Calls the appropriate ScreenBuilder with the screen's dimensions + */ + public static void render(DrawContext context, int w, int h) { + String location = PlayerLocator.getPlayerLocation().internal; + HashMap lookup; + if (TabHud.toggleA.isPressed()) { + lookup = screenAMap; + } else if (TabHud.toggleB.isPressed()) { + lookup = screenBMap; + } else { + lookup = standardMap; + } + + ScreenBuilder sb = lookup.get(location); + // seems suboptimal, maybe load the default first into all possible values + // and then override? + if (sb == null) { + sb = lookup.get("default"); + } + + sb.run(context, w, h); + + } + + public static void init() { + + // WHY MUST IT ALWAYS BE SUCH NESTED GARBAGE MINECRAFT KEEP THAT IN DFU FFS + + FabricLoader.getInstance() + .getModContainer("skyblocker") + .ifPresent(container -> ResourceManagerHelper.registerBuiltinResourcePack( + new Identifier("skyblocker", "top_aligned"), + container, + ResourcePackActivationType.NORMAL)); + + ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener( + // ...why are we instantiating an interface again? + new SimpleSynchronousResourceReloadListener() { + @Override + public Identifier getFabricId() { + return new Identifier("skyblocker", "tabhud"); + } + + @Override + public void reload(ResourceManager manager) { + + standardMap.clear(); + screenAMap.clear(); + screenBMap.clear(); + + int excnt = 0; + + for (Map.Entry entry : manager + .findResources("tabhud", path -> path.getPath().endsWith("version.json")) + .entrySet()) { + + try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager() + .openAsReader(entry.getKey())) { + JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); + if (json.get("format_version").getAsInt() != VERSION) { + throw new IllegalStateException(String.format("Resource pack isn't compatible! Expected version %d, got %d", VERSION, json.get("format_version").getAsInt())); + } + + } catch (Exception ex) { + throw new IllegalStateException( + "Rejected this resource pack. Reason: " + ex.getMessage()); + } + } + + for (Map.Entry entry : manager + .findResources("tabhud", path -> path.getPath().endsWith(".json") && !path.getPath().endsWith("version.json")) + .entrySet()) { + try { + + load(entry.getKey()); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + excnt++; + } + } + if (excnt > 0) { + throw new IllegalStateException("This screen definition isn't valid, see above"); + } + } + }); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java new file mode 100644 index 00000000..7c01a6db --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java @@ -0,0 +1,83 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class AlignStage extends PipelineStage { + + private enum AlignReference { + HORICENT("horizontalCenter"), + VERTCENT("verticalCenter"), + LEFTCENT("leftOfCenter"), + RIGHTCENT("rightOfCenter"), + TOPCENT("topOfCenter"), + BOTCENT("botOfCenter"), + TOP("top"), + BOT("bot"), + LEFT("left"), + RIGHT("right"); + + private final String str; + + AlignReference(String d) { + this.str = d; + } + + public static AlignReference parse(String s) throws NoSuchElementException { + for (AlignReference d : AlignReference.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid reference for an align op!"); + } + } + + private final AlignReference reference; + + public AlignStage(ScreenBuilder builder, JsonObject descr) { + this.reference = AlignReference.parse(descr.get("reference").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + } + + public void run(int screenW, int screenH) { + int wHalf, hHalf; + for (Widget wid : primary) { + switch (this.reference) { + case HORICENT -> wid.setX((screenW - wid.getWidth()) / 2); + case VERTCENT -> wid.setY((screenH - wid.getHeight()) / 2); + case LEFTCENT -> { + wHalf = screenW / 2; + wid.setX(wHalf - ScreenConst.WIDGET_PAD_HALF - wid.getWidth()); + } + case RIGHTCENT -> { + wHalf = screenW / 2; + wid.setX(wHalf + ScreenConst.WIDGET_PAD_HALF); + } + case TOPCENT -> { + hHalf = screenH / 2; + wid.setY(hHalf - ScreenConst.WIDGET_PAD_HALF - wid.getHeight()); + } + case BOTCENT -> { + hHalf = screenH / 2; + wid.setY(hHalf + ScreenConst.WIDGET_PAD_HALF); + } + case TOP -> wid.setY(ScreenConst.getScreenPad()); + case BOT -> wid.setY(screenH - wid.getHeight() - ScreenConst.getScreenPad()); + case LEFT -> wid.setX(ScreenConst.getScreenPad()); + case RIGHT -> wid.setX(screenW - wid.getWidth() - ScreenConst.getScreenPad()); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java new file mode 100644 index 00000000..d100a52e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java @@ -0,0 +1,153 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class CollideStage extends PipelineStage { + + private enum CollideDirection { + LEFT("left"), + RIGHT("right"), + TOP("top"), + BOT("bot"); + + private final String str; + + CollideDirection(String d) { + this.str = d; + } + + public static CollideDirection parse(String s) throws NoSuchElementException { + for (CollideDirection d : CollideDirection.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a collide op!"); + } + } + + private final CollideDirection direction; + + public CollideStage(ScreenBuilder builder, JsonObject descr) { + this.direction = CollideDirection.parse(descr.get("direction").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("widgets") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + this.secondary = new ArrayList<>(descr.getAsJsonArray("colliders") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + } + + public void run(int screenW, int screenH) { + switch (this.direction) { + case LEFT -> primary.forEach(w -> collideAgainstL(screenW, w)); + case RIGHT -> primary.forEach(w -> collideAgainstR(screenW, w)); + case TOP -> primary.forEach(w -> collideAgainstT(screenH, w)); + case BOT -> primary.forEach(w -> collideAgainstB(screenH, w)); + } + } + + public void collideAgainstL(int screenW, Widget w) { + int yMin = w.getY(); + int yMax = w.getY() + w.getHeight(); + + int xCor = screenW; + + for (Widget other : secondary) { + if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) { + // too high, next one + continue; + } + + if (other.getY() - ScreenConst.WIDGET_PAD > yMax) { + // too low, next + continue; + } + + int xPos = other.getX() - ScreenConst.WIDGET_PAD - w.getWidth(); + xCor = Math.min(xCor, xPos); + } + w.setX(xCor); + } + + public void collideAgainstR(int screenW, Widget w) { + int yMin = w.getY(); + int yMax = w.getY() + w.getHeight(); + + int xCor = 0; + + for (Widget other : secondary) { + if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) { + // too high, next one + continue; + } + + if (other.getY() - ScreenConst.WIDGET_PAD > yMax) { + // too low, next + continue; + } + + int xPos = other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD; + xCor = Math.max(xCor, xPos); + } + w.setX(xCor); + } + + public void collideAgainstT(int screenH, Widget w) { + int xMin = w.getX(); + int xMax = w.getX() + w.getWidth(); + + int yCor = screenH; + + for (Widget other : secondary) { + if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) { + // too far left, next one + continue; + } + + if (other.getX() - ScreenConst.WIDGET_PAD > xMax) { + // too far right, next + continue; + } + + int yPos = other.getY() - ScreenConst.WIDGET_PAD - w.getHeight(); + yCor = Math.min(yCor, yPos); + } + w.setY(yCor); + } + + public void collideAgainstB(int screenH, Widget w) { + int xMin = w.getX(); + int xMax = w.getX() + w.getWidth(); + + int yCor = 0; + + for (Widget other : secondary) { + if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) { + // too far left, next one + continue; + } + + if (other.getX() - ScreenConst.WIDGET_PAD > xMax) { + // too far right, next + continue; + } + + int yPos = other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD; + yCor = Math.max(yCor, yPos); + } + w.setY(yCor); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java new file mode 100644 index 00000000..20e4859e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; + +public abstract class PipelineStage { + + protected ArrayList primary = null; + protected ArrayList secondary = null; + + public abstract void run(int screenW, int screenH); + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java new file mode 100644 index 00000000..7d57305b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java @@ -0,0 +1,94 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class PlaceStage extends PipelineStage { + + private enum PlaceLocation { + CENTER("center"), + TOPCENT("centerTop"), + BOTCENT("centerBot"), + LEFTCENT("centerLeft"), + RIGHTCENT("centerRight"), + TRCORNER("cornerTopRight"), + TLCORNER("cornerTopLeft"), + BRCORNER("cornerBotRight"), + BLCORNER("cornerBotLeft"); + + private final String str; + + PlaceLocation(String d) { + this.str = d; + } + + public static PlaceLocation parse(String s) throws NoSuchElementException { + for (PlaceLocation d : PlaceLocation.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid location for a place op!"); + } + } + + private final PlaceLocation where; + + public PlaceStage(ScreenBuilder builder, JsonObject descr) { + this.where = PlaceLocation.parse(descr.get("where").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .limit(1) + .toList()); + } + + public void run(int screenW, int screenH) { + Widget wid = primary.get(0); + switch (where) { + case CENTER -> { + wid.setX((screenW - wid.getWidth()) / 2); + wid.setY((screenH - wid.getHeight()) / 2); + } + case TOPCENT -> { + wid.setX((screenW - wid.getWidth()) / 2); + wid.setY(ScreenConst.getScreenPad()); + } + case BOTCENT -> { + wid.setX((screenW - wid.getWidth()) / 2); + wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); + } + case LEFTCENT -> { + wid.setX(ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) / 2); + } + case RIGHTCENT -> { + wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) / 2); + } + case TLCORNER -> { + wid.setX(ScreenConst.getScreenPad()); + wid.setY(ScreenConst.getScreenPad()); + } + case TRCORNER -> { + wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); + wid.setY(ScreenConst.getScreenPad()); + } + case BLCORNER -> { + wid.setX(ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); + } + case BRCORNER -> { + wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); + wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java new file mode 100644 index 00000000..f4fe07e5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java @@ -0,0 +1,114 @@ +package de.hysky.skyblocker.skyblock.tabhud.screenbuilder.pipeline; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +import com.google.gson.JsonObject; + +import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.ScreenConst; + +public class StackStage extends PipelineStage { + + private enum StackDirection { + HORIZONTAL("horizontal"), + VERTICAL("vertical"); + + private final String str; + + StackDirection(String d) { + this.str = d; + } + + public static StackDirection parse(String s) throws NoSuchElementException { + for (StackDirection d : StackDirection.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a stack op!"); + } + } + + private enum StackAlign { + TOP("top"), + BOT("bot"), + LEFT("left"), + RIGHT("right"), + CENTER("center"); + + private final String str; + + StackAlign(String d) { + this.str = d; + } + + public static StackAlign parse(String s) throws NoSuchElementException { + for (StackAlign d : StackAlign.values()) { + if (d.str.equals(s)) { + return d; + } + } + throw new NoSuchElementException("\"" + s + "\" is not a valid alignment for a stack op!"); + } + } + + private final StackDirection direction; + private final StackAlign align; + + public StackStage(ScreenBuilder builder, JsonObject descr) { + this.direction = StackDirection.parse(descr.get("direction").getAsString()); + this.align = StackAlign.parse(descr.get("align").getAsString()); + this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") + .asList() + .stream() + .map(x -> builder.getInstance(x.getAsString())) + .toList()); + } + + public void run(int screenW, int screenH) { + switch (this.direction) { + case HORIZONTAL -> stackWidgetsHoriz(screenW); + case VERTICAL -> stackWidgetsVert(screenH); + } + } + + public void stackWidgetsVert(int screenH) { + int compHeight = -ScreenConst.WIDGET_PAD; + for (Widget wid : primary) { + compHeight += wid.getHeight() + 5; + } + + int y = switch (this.align) { + + case TOP -> ScreenConst.getScreenPad(); + case BOT -> (screenH - compHeight) - ScreenConst.getScreenPad(); + default -> (screenH - compHeight) / 2; + }; + + for (Widget wid : primary) { + wid.setY(y); + y += wid.getHeight() + ScreenConst.WIDGET_PAD; + } + } + + public void stackWidgetsHoriz(int screenW) { + int compWidth = -ScreenConst.WIDGET_PAD; + for (Widget wid : primary) { + compWidth += wid.getWidth() + ScreenConst.WIDGET_PAD; + } + + int x = switch (this.align) { + + case LEFT -> ScreenConst.getScreenPad(); + case RIGHT -> (screenW - compWidth) - ScreenConst.getScreenPad(); + default -> (screenW - compWidth) / 2; + }; + + for (Widget wid : primary) { + wid.setX(x); + x += wid.getWidth() + ScreenConst.WIDGET_PAD; + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java new file mode 100644 index 00000000..24883d77 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java @@ -0,0 +1,60 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; + +/** + * Stores convenient shorthands for common ItemStack definitions + */ +public class Ico { + public static final ItemStack MAP = new ItemStack(Items.FILLED_MAP); + public static final ItemStack NTAG = new ItemStack(Items.NAME_TAG); + public static final ItemStack EMERALD = new ItemStack(Items.EMERALD); + public static final ItemStack CLOCK = new ItemStack(Items.CLOCK); + public static final ItemStack DIASWORD = new ItemStack(Items.DIAMOND_SWORD); + public static final ItemStack DBUSH = new ItemStack(Items.DEAD_BUSH); + public static final ItemStack VILLAGER = new ItemStack(Items.VILLAGER_SPAWN_EGG); + public static final ItemStack MOREGOLD = new ItemStack(Items.GOLDEN_APPLE); + public static final ItemStack COMPASS = new ItemStack(Items.COMPASS); + public static final ItemStack SUGAR = new ItemStack(Items.SUGAR); + public static final ItemStack HOE = new ItemStack(Items.IRON_HOE); + public static final ItemStack GOLD = new ItemStack(Items.GOLD_INGOT); + public static final ItemStack BONE = new ItemStack(Items.BONE); + public static final ItemStack SIGN = new ItemStack(Items.OAK_SIGN); + public static final ItemStack FISH_ROD = new ItemStack(Items.FISHING_ROD); + public static final ItemStack SWORD = new ItemStack(Items.IRON_SWORD); + public static final ItemStack LANTERN = new ItemStack(Items.LANTERN); + public static final ItemStack COOKIE = new ItemStack(Items.COOKIE); + public static final ItemStack POTION = new ItemStack(Items.POTION); + public static final ItemStack BARRIER = new ItemStack(Items.BARRIER); + public static final ItemStack PLAYER = new ItemStack(Items.PLAYER_HEAD); + public static final ItemStack WATER = new ItemStack(Items.WATER_BUCKET); + public static final ItemStack LEATHER = new ItemStack(Items.LEATHER); + public static final ItemStack MITHRIL = new ItemStack(Items.PRISMARINE_CRYSTALS); + public static final ItemStack REDSTONE = new ItemStack(Items.REDSTONE); + public static final ItemStack FIRE = new ItemStack(Items.CAMPFIRE); + public static final ItemStack STRING = new ItemStack(Items.STRING); + public static final ItemStack WITHER = new ItemStack(Items.WITHER_SKELETON_SKULL); + public static final ItemStack FLESH = new ItemStack(Items.ROTTEN_FLESH); + public static final ItemStack DRAGON = new ItemStack(Items.DRAGON_HEAD); + public static final ItemStack DIAMOND = new ItemStack(Items.DIAMOND); + public static final ItemStack ICE = new ItemStack(Items.ICE); + public static final ItemStack CHEST = new ItemStack(Items.CHEST); + public static final ItemStack COMMAND = new ItemStack(Items.COMMAND_BLOCK); + public static final ItemStack SKULL = new ItemStack(Items.SKELETON_SKULL); + public static final ItemStack BOOK = new ItemStack(Items.WRITABLE_BOOK); + public static final ItemStack FURNACE = new ItemStack(Items.FURNACE); + public static final ItemStack CHESTPLATE = new ItemStack(Items.IRON_CHESTPLATE); + public static final ItemStack B_ROD = new ItemStack(Items.BLAZE_ROD); + public static final ItemStack BOW = new ItemStack(Items.BOW); + public static final ItemStack COPPER = new ItemStack(Items.COPPER_INGOT); + public static final ItemStack COMPOSTER = new ItemStack(Items.COMPOSTER); + public static final ItemStack SAPLING = new ItemStack(Items.OAK_SAPLING); + public static final ItemStack MILESTONE = new ItemStack(Items.LODESTONE); + public static final ItemStack PICKAXE = new ItemStack(Items.IRON_PICKAXE); + public static final ItemStack NETHER_STAR = new ItemStack(Items.NETHER_STAR); + public static final ItemStack HEART_OF_THE_SEA = new ItemStack(Items.HEART_OF_THE_SEA); + public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE); + public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE); + public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java new file mode 100644 index 00000000..f577f2d3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerListMgr.java @@ -0,0 +1,171 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.mixin.accessor.PlayerListHudAccessor; +import de.hysky.skyblocker.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; + +/** + * This class may be used to get data from the player list. It doesn't get its + * data every frame, instead, a scheduler is used to update the data this class + * is holding periodically. The list is sorted like in the vanilla game. + */ +public class PlayerListMgr { + + public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Regex"); + + private static List playerList; + private static String footer; + + public static void updateList() { + + if (!Utils.isOnSkyblock()) { + return; + } + + ClientPlayNetworkHandler cpnwh = MinecraftClient.getInstance().getNetworkHandler(); + + // check is needed, else game crash on server leave + if (cpnwh != null) { + playerList = cpnwh.getPlayerList().stream().sorted(PlayerListHudAccessor.getOrdering()).toList(); + } + } + + public static void updateFooter(Text f) { + if (f == null) { + footer = null; + } else { + footer = f.getString(); + } + } + + public static String getFooter() { + return footer; + } + + /** + * Get the display name at some index of the player list and apply a pattern to + * it + * + * @return the matcher if p fully matches, else null + */ + public static Matcher regexAt(int idx, Pattern p) { + + String str = PlayerListMgr.strAt(idx); + + if (str == null) { + return null; + } + + Matcher m = p.matcher(str); + if (!m.matches()) { + LOGGER.error("no match: \"{}\" against \"{}\"", str, p); + return null; + } else { + return m; + } + } + + /** + * Get the display name at some index of the player list as string + * + * @return the string or null, if the display name is null, empty or whitespace + * only + */ + public static String strAt(int idx) { + + if (playerList == null) { + return null; + } + + if (playerList.size() <= idx) { + return null; + } + + Text txt = playerList.get(idx).getDisplayName(); + if (txt == null) { + return null; + } + String str = txt.getString().trim(); + if (str.isEmpty()) { + return null; + } + return str; + } + + /** + * Gets the display name at some index of the player list + * + * @return the text or null, if the display name is null + * + * @implNote currently designed specifically for crimson isles faction quests + * widget and the rift widgets, might not work correctly without + * modification for other stuff. you've been warned! + */ + public static Text textAt(int idx) { + + if (playerList == null) { + return null; + } + + if (playerList.size() <= idx) { + return null; + } + + Text txt = playerList.get(idx).getDisplayName(); + if (txt == null) { + return null; + } + + // Rebuild the text object to remove leading space thats in all faction quest + // stuff (also removes trailing space just in case) + MutableText newText = Text.empty(); + int size = txt.getSiblings().size(); + + for (int i = 0; i < size; i++) { + Text current = txt.getSiblings().get(i); + String textToAppend = current.getString(); + + // Trim leading & trailing space - this can only be done at the start and end + // otherwise it'll produce malformed results + if (i == 0) + textToAppend = textToAppend.stripLeading(); + if (i == size - 1) + textToAppend = textToAppend.stripTrailing(); + + newText.append(Text.literal(textToAppend).setStyle(current.getStyle())); + } + + // Avoid returning an empty component - Rift advertisements needed this + if (newText.getString().isEmpty()) { + return null; + } + + return newText; + } + + /** + * Get the display name at some index of the player list as Text as seen in the + * game + * + * @return the PlayerListEntry at that index + */ + public static PlayerListEntry getRaw(int idx) { + return playerList.get(idx); + } + + public static int getSize() { + return playerList.size(); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java new file mode 100644 index 00000000..e5f5bfc8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/PlayerLocator.java @@ -0,0 +1,87 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import de.hysky.skyblocker.utils.Utils; + +/** + * Uses data from the player list to determine the area the player is in. + */ +public class PlayerLocator { + + public enum Location { + DUNGEON("dungeon"), + GUEST_ISLAND("guest_island"), + HOME_ISLAND("home_island"), + CRIMSON_ISLE("crimson_isle"), + DUNGEON_HUB("dungeon_hub"), + FARMING_ISLAND("farming_island"), + PARK("park"), + DWARVEN_MINES("dwarven_mines"), + CRYSTAL_HOLLOWS("crystal_hollows"), + END("end"), + GOLD_MINE("gold_mine"), + DEEP_CAVERNS("deep_caverns"), + HUB("hub"), + SPIDER_DEN("spider_den"), + JERRY("jerry_workshop"), + GARDEN("garden"), + INSTANCED("kuudra"), + THE_RIFT("rift"), + DARK_AUCTION("dark_auction"), + UNKNOWN("unknown"); + + public final String internal; + + Location(String i) { + // as used internally by the mod, e.g. in the json + this.internal = i; + } + + } + + public static Location getPlayerLocation() { + + if (!Utils.isOnSkyblock()) { + return Location.UNKNOWN; + } + + String areaDescriptor = PlayerListMgr.strAt(41); + + if (areaDescriptor == null || areaDescriptor.length() < 6) { + return Location.UNKNOWN; + } + + if (areaDescriptor.startsWith("Dungeon")) { + return Location.DUNGEON; + } + + return switch (areaDescriptor.substring(6)) { + case "Private Island" -> { + String islandType = PlayerListMgr.strAt(44); + if (islandType == null) { + yield Location.UNKNOWN; + } else if (islandType.endsWith("Guest")) { + yield Location.GUEST_ISLAND; + } else { + yield Location.HOME_ISLAND; + } + } + case "Crimson Isle" -> Location.CRIMSON_ISLE; + case "Dungeon Hub" -> Location.DUNGEON_HUB; + case "The Farming Islands" -> Location.FARMING_ISLAND; + case "The Park" -> Location.PARK; + case "Dwarven Mines" -> Location.DWARVEN_MINES; + case "Crystal Hollows" -> Location.CRYSTAL_HOLLOWS; + case "The End" -> Location.END; + case "Gold Mine" -> Location.GOLD_MINE; + case "Deep Caverns" -> Location.DEEP_CAVERNS; + case "Hub" -> Location.HUB; + case "Spider's Den" -> Location.SPIDER_DEN; + case "Jerry's Workshop" -> Location.JERRY; + case "Garden" -> Location.GARDEN; + case "Instanced" -> Location.INSTANCED; + case "The Rift" -> Location.THE_RIFT; + case "Dark Auction" -> Location.DARK_AUCTION; + default -> Location.UNKNOWN; + }; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java new file mode 100644 index 00000000..6a4d96d3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java @@ -0,0 +1,13 @@ +package de.hysky.skyblocker.skyblock.tabhud.util; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; + +public class ScreenConst { + public static final int WIDGET_PAD = 5; + public static final int WIDGET_PAD_HALF = 3; + private static final int SCREEN_PAD_BASE = 20; + + public static int getScreenPad() { + return (int) ((1f/((float)SkyblockerConfigManager.get().general.tabHud.tabHudScale/100f) * SCREEN_PAD_BASE)); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java new file mode 100644 index 00000000..9cff3d32 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java @@ -0,0 +1,37 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +public class CameraPositionWidget extends Widget { + private static final MutableText TITLE = Text.literal("Camera Pos").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + + public CameraPositionWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + double yaw = CLIENT.getCameraEntity().getYaw(); + double pitch = CLIENT.getCameraEntity().getPitch(); + + this.addComponent( + new PlainTextComponent(Text.literal("Yaw: " + roundToDecimalPlaces(MathHelper.wrapDegrees(yaw), 3)))); + this.addComponent(new PlainTextComponent( + Text.literal("Pitch: " + roundToDecimalPlaces(MathHelper.wrapDegrees(pitch), 3)))); + + } + + // https://stackoverflow.com/a/33889423 + private static double roundToDecimalPlaces(double value, int decimalPlaces) { + double shift = Math.pow(10, decimalPlaces); + + return Math.round(value * shift) / shift; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java new file mode 100644 index 00000000..e8bf91ab --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CommsWidget.java @@ -0,0 +1,63 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +// this widget shows the status of the king's commissions. +// (dwarven mines and crystal hollows) + +public class CommsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + // match a comm + // group 1: comm name + // group 2: comm progress (without "%" for comms that show a percentage) + private static final Pattern COMM_PATTERN = Pattern.compile("(?.*): (?.*)%?"); + + public CommsWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + for (int i = 50; i <= 53; i++) { + Matcher m = PlayerListMgr.regexAt(i, COMM_PATTERN); + // end of comms found? + if (m == null) { + if (i == 50) { + this.addComponent(new IcoTextComponent()); + } + break; + } + + ProgressComponent pc; + + String name = m.group("name"); + String progress = m.group("progress"); + + if (progress.equals("DONE")) { + pc = new ProgressComponent(Ico.BOOK, Text.of(name), Text.of(progress), 100f, pcntToCol(100)); + } else { + float pcnt = Float.parseFloat(progress.substring(0, progress.length() - 1)); + pc = new ProgressComponent(Ico.BOOK, Text.of(name), pcnt, pcntToCol(pcnt)); + } + this.addComponent(pc); + } + } + + private int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java new file mode 100644 index 00000000..fbeb5ae5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComposterWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the garden's composter + +public class ComposterWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Composter").formatted(Formatting.GREEN, + Formatting.BOLD); + + public ComposterWidget() { + super(TITLE, Formatting.GREEN.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.SAPLING, "Organic Matter:", Formatting.YELLOW, 48); + this.addSimpleIcoText(Ico.FURNACE, "Fuel:", Formatting.BLUE, 49); + this.addSimpleIcoText(Ico.CLOCK, "Time Left:", Formatting.RED, 50); + this.addSimpleIcoText(Ico.COMPOSTER, "Stored Compost:", Formatting.DARK_GREEN, 51); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java new file mode 100644 index 00000000..a5883e7e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/CookieWidget.java @@ -0,0 +1,50 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about active super cookies +// or not, if you're unwilling to buy one + +public class CookieWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Cookie Info").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + private static final Pattern COOKIE_PATTERN = Pattern.compile(".*\\nCookie Buff\\n(?.*)\\n"); + + public CookieWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + String footertext = PlayerListMgr.getFooter(); + if (footertext == null || !footertext.contains("Cookie Buff")) { + this.addComponent(new IcoTextComponent()); + return; + } + + Matcher m = COOKIE_PATTERN.matcher(footertext); + if (!m.find() || m.group("buff") == null) { + this.addComponent(new IcoTextComponent()); + return; + } + + String buff = m.group("buff"); + if (buff.startsWith("Not")) { + this.addComponent(new IcoTextComponent(Ico.COOKIE, Text.of("Not active"))); + } else { + Text cookie = Text.literal("Time Left: ").append(buff); + this.addComponent(new IcoTextComponent(Ico.COOKIE, cookie)); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java new file mode 100644 index 00000000..fd896796 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java @@ -0,0 +1,68 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.Arrays; +import java.util.Comparator; + +// this widget shows a list of obtained dungeon buffs + +public class DungeonBuffWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Dungeon Buffs").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public DungeonBuffWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + + String footertext = PlayerListMgr.getFooter(); + + if (footertext == null || !footertext.contains("Dungeon Buffs")) { + this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY))); + return; + } + + String interesting = footertext.split("Dungeon Buffs")[1]; + String[] lines = interesting.split("\n"); + + if (!lines[1].startsWith("Blessing")) { + this.addComponent(new PlainTextComponent(Text.literal("No buffs found!").formatted(Formatting.GRAY))); + return; + } + + //Filter out text unrelated to blessings + lines = Arrays.stream(lines).filter(s -> s.contains("Blessing")).toArray(String[]::new); + + //Alphabetically sort the blessings + Arrays.sort(lines, Comparator.comparing(String::toLowerCase)); + + for (String line : lines) { + if (line.length() < 3) { // empty line is §s + break; + } + int color = getBlessingColor(line); + this.addComponent(new PlainTextComponent(Text.literal(line).styled(style -> style.withColor(color)))); + } + + } + + @SuppressWarnings("DataFlowIssue") + public int getBlessingColor(String blessing) { + if (blessing.contains("Life")) return Formatting.LIGHT_PURPLE.getColorValue(); + if (blessing.contains("Power")) return Formatting.RED.getColorValue(); + if (blessing.contains("Stone")) return Formatting.GREEN.getColorValue(); + if (blessing.contains("Time")) return 0xafb8c1; + if (blessing.contains("Wisdom")) return Formatting.AQUA.getColorValue(); + + return 0xffffff; + } + +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java new file mode 100644 index 00000000..9c299210 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows various dungeon info +// deaths, healing, dmg taken, milestones + +public class DungeonDeathWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Death").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match the deaths entry + // group 1: amount of deaths + private static final Pattern DEATH_PATTERN = Pattern.compile("Team Deaths: (?\\d+).*"); + + public DungeonDeathWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + Matcher m = PlayerListMgr.regexAt(25, DEATH_PATTERN); + if (m == null) { + this.addComponent(new IcoTextComponent()); + } else { + Formatting f = (m.group("deathnum").equals("0")) ? Formatting.GREEN : Formatting.RED; + Text d = Widget.simpleEntryText(m.group("deathnum"), "Deaths: ", f); + IcoTextComponent deaths = new IcoTextComponent(Ico.SKULL, d); + this.addComponent(deaths); + } + + this.addSimpleIcoText(Ico.SWORD, "Damage Dealt:", Formatting.RED, 26); + this.addSimpleIcoText(Ico.POTION, "Healing Done:", Formatting.RED, 27); + this.addSimpleIcoText(Ico.NTAG, "Milestone:", Formatting.YELLOW, 28); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java new file mode 100644 index 00000000..9a8de0eb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java @@ -0,0 +1,44 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about... something? +// related to downed people in dungeons, not sure what this is supposed to show + +public class DungeonDownedWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Downed").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public DungeonDownedWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + String down = PlayerListMgr.strAt(21); + if (down == null) { + this.addComponent(new IcoTextComponent()); + } else { + + Formatting format = Formatting.RED; + if (down.endsWith("NONE")) { + format = Formatting.GRAY; + } + int idx = down.indexOf(": "); + Text downed = (idx == -1) ? null + : Widget.simpleEntryText(down.substring(idx + 2), "Downed: ", format); + IcoTextComponent d = new IcoTextComponent(Ico.SKULL, downed); + this.addComponent(d); + } + + this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GRAY, 22); + this.addSimpleIcoText(Ico.POTION, "Revive:", Formatting.GRAY, 23); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java new file mode 100644 index 00000000..be1a3c6e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java @@ -0,0 +1,103 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about a player in the current dungeon group + +public class DungeonPlayerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Player").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match a player entry + // group 1: name + // group 2: class (or literal "EMPTY" pre dungeon start) + // group 3: level (or nothing, if pre dungeon start) + // this regex filters out the ironman icon as well as rank prefixes and emblems + // \[\d*\] (?:\[[A-Za-z]+\] )?(?[A-Za-z0-9_]*) (?:.* )?\((?\S*) ?(?[LXVI]*)\) + private static final Pattern PLAYER_PATTERN = Pattern + .compile("\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?(?[A-Za-z0-9_]*) (?:.* )?\\((?\\S*) ?(?[LXVI]*)\\)"); + + private static final HashMap ICOS = new HashMap<>(); + private static final ArrayList MSGS = new ArrayList<>(); + static { + ICOS.put("Tank", Ico.CHESTPLATE); + ICOS.put("Mage", Ico.B_ROD); + ICOS.put("Berserk", Ico.DIASWORD); + ICOS.put("Archer", Ico.BOW); + ICOS.put("Healer", Ico.POTION); + + MSGS.add("PRESS A TO JOIN"); + MSGS.add("Invite a friend!"); + MSGS.add("But nobody came."); + MSGS.add("More is better!"); + } + + private final int player; + + // title needs to be changeable here + public DungeonPlayerWidget(int player) { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + this.player = player; + } + + @Override + public void updateContent() { + int start = 1 + (player - 1) * 4; + + if (PlayerListMgr.strAt(start) == null) { + int idx = player - 2; + IcoTextComponent noplayer = new IcoTextComponent(Ico.SIGN, + Text.literal(MSGS.get(idx)).formatted(Formatting.GRAY)); + this.addComponent(noplayer); + return; + } + Matcher m = PlayerListMgr.regexAt(start, PLAYER_PATTERN); + if (m == null) { + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + } else { + + Text name = Text.literal("Name: ").append(Text.literal(m.group("name")).formatted(Formatting.YELLOW)); + this.addComponent(new IcoTextComponent(Ico.PLAYER, name)); + + String cl = m.group("class"); + String level = m.group("level"); + + if (level == null) { + PlainTextComponent ptc = new PlainTextComponent( + Text.literal("Player is dead").formatted(Formatting.RED)); + this.addComponent(ptc); + } else { + + Formatting clf = Formatting.GRAY; + ItemStack cli = Ico.BARRIER; + if (!cl.equals("EMPTY")) { + cli = ICOS.get(cl); + clf = Formatting.LIGHT_PURPLE; + cl += " " + m.group("level"); + } + + Text clazz = Text.literal("Class: ").append(Text.literal(cl).formatted(clf)); + IcoTextComponent itclass = new IcoTextComponent(cli, clazz); + this.addComponent(itclass); + } + } + + this.addSimpleIcoText(Ico.CLOCK, "Ult Cooldown:", Formatting.GOLD, start + 1); + this.addSimpleIcoText(Ico.POTION, "Revives:", Formatting.DARK_PURPLE, start + 2); + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java new file mode 100644 index 00000000..1b3b8644 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java @@ -0,0 +1,57 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about all puzzeles in the dungeon (name and status) + +public class DungeonPuzzleWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Puzzles").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match a puzzle entry + // group 1: name + // group 2: status + // " ?.*" to diescard the solver's name if present + // the teleport maze has a trailing whitespace that messes with the regex + private static final Pattern PUZZLE_PATTERN = Pattern.compile("(?.*): \\[(?.*)\\] ?.*"); + + public DungeonPuzzleWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + int pos = 48; + + while (pos < 60) { + Matcher m = PlayerListMgr.regexAt(pos, PUZZLE_PATTERN); + if (m == null) { + break; + } + Text t = Text.literal(m.group("name") + ": ") + .append(Text.literal("[").formatted(Formatting.GRAY)) + .append(m.group("status")) + .append(Text.literal("]").formatted(Formatting.GRAY)); + IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, t); + this.addComponent(itc); + pos++; + // code points for puzzle status chars unsolved and solved: 10022, 10004 + // not sure which one is which + // still need to find out codepoint for the puzzle failed char + } + if (pos == 48) { + this.addComponent( + new IcoTextComponent(Ico.BARRIER, Text.literal("No puzzles!").formatted(Formatting.GRAY))); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java new file mode 100644 index 00000000..6f40f5a8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java @@ -0,0 +1,26 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the secrets of the dungeon + +public class DungeonSecretWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public DungeonSecretWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31); + this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java new file mode 100644 index 00000000..569987e8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java @@ -0,0 +1,48 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows broad info about the current dungeon +// opened/completed rooms, % of secrets found and time taken + +public class DungeonServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Dungeon Info").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // match the secrets text + // group 1: % of secrets found (without "%") + private static final Pattern SECRET_PATTERN = Pattern.compile("Secrets Found: (?.*)%"); + + public DungeonServerWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.AQUA, 41); + this.addSimpleIcoText(Ico.SIGN, "Rooms Visited:", Formatting.DARK_PURPLE, 42); + this.addSimpleIcoText(Ico.SIGN, "Rooms Completed:", Formatting.LIGHT_PURPLE, 43); + + Matcher m = PlayerListMgr.regexAt(44, SECRET_PATTERN); + if (m == null) { + this.addComponent(new ProgressComponent()); + } else { + ProgressComponent scp = new ProgressComponent(Ico.CHEST, Text.of("Secrets found:"), + Float.parseFloat(m.group("secnum")), + Formatting.DARK_PURPLE.getColorValue()); + this.addComponent(scp); + } + + this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GOLD, 45); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java new file mode 100644 index 00000000..5ec3faf1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EffectWidget.java @@ -0,0 +1,67 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widgte shows, how many active effects you have. +// it also shows one of those in detail. +// the parsing is super suspect and should be replaced by some regexes sometime later + +public class EffectWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Effect Info").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + public EffectWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + + String footertext = PlayerListMgr.getFooter(); + + if (footertext == null || !footertext.contains("Active Effects")) { + this.addComponent(new IcoTextComponent()); + return; + + } + + String[] lines = footertext.split("Active Effects")[1].split("\n"); + if (lines.length < 2) { + this.addComponent(new IcoTextComponent()); + return; + } + + if (lines[1].startsWith("No")) { + Text txt = Text.literal("No effects active").formatted(Formatting.GRAY); + this.addComponent(new IcoTextComponent(Ico.POTION, txt)); + } else if (lines[1].contains("God")) { + String timeleft = lines[1].split("! ")[1]; + Text godpot = Text.literal("God potion!").formatted(Formatting.RED); + Text txttleft = Text.literal(timeleft).formatted(Formatting.LIGHT_PURPLE); + IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, godpot, txttleft); + this.addComponent(iftc); + } else { + String number = lines[1].substring("You have ".length()); + int idx = number.indexOf(' '); + if (idx == -1 || lines.length < 4) { + this.addComponent(new IcoFatTextComponent()); + return; + } + number = number.substring(0, idx); + Text active = Text.literal("Active Effects: ") + .append(Text.literal(number).formatted(Formatting.YELLOW)); + + IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, active, + Text.literal(lines[3]).formatted(Formatting.AQUA)); + this.addComponent(iftc); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java new file mode 100644 index 00000000..ec935faf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ElectionWidget.java @@ -0,0 +1,104 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows the status or results of the current election + +public class ElectionWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Election Info").formatted(Formatting.YELLOW, + Formatting.BOLD); + + private static final HashMap MAYOR_DATA = new HashMap<>(); + + private static final Text EL_OVER = Text.literal("Election ") + .append(Text.literal("over!").formatted(Formatting.RED)); + + // pattern matching a candidate while people are voting + // group 1: name + // group 2: % of votes + private static final Pattern VOTE_PATTERN = Pattern.compile("(?\\S*): \\|+ \\((?\\d*)%\\)"); + + static { + MAYOR_DATA.put("Aatrox", Ico.DIASWORD); + MAYOR_DATA.put("Cole", Ico.PICKAXE); + MAYOR_DATA.put("Diana", Ico.BONE); + MAYOR_DATA.put("Diaz", Ico.GOLD); + MAYOR_DATA.put("Finnegan", Ico.HOE); + MAYOR_DATA.put("Foxy", Ico.SUGAR); + MAYOR_DATA.put("Paul", Ico.COMPASS); + MAYOR_DATA.put("Scorpius", Ico.MOREGOLD); + MAYOR_DATA.put("Jerry", Ico.VILLAGER); + MAYOR_DATA.put("Derpy", Ico.DBUSH); + MAYOR_DATA.put("Marina", Ico.FISH_ROD); + } + + private static final Formatting[] COLS = { Formatting.GOLD, Formatting.RED, Formatting.LIGHT_PURPLE }; + + public ElectionWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + } + + @Override + public void updateContent() { + String status = PlayerListMgr.strAt(76); + if (status == null) { + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + this.addComponent(new IcoTextComponent()); + return; + } + + if (status.contains("Over!")) { + // election is over + IcoTextComponent over = new IcoTextComponent(Ico.BARRIER, EL_OVER); + this.addComponent(over); + + String win = PlayerListMgr.strAt(77); + if (win == null || !win.contains(": ")) { + this.addComponent(new IcoTextComponent()); + } else { + String winnername = win.split(": ")[1]; + Text winnertext = Widget.simpleEntryText(winnername, "Winner: ", Formatting.GREEN); + IcoTextComponent winner = new IcoTextComponent(MAYOR_DATA.get(winnername), winnertext); + this.addComponent(winner); + } + + this.addSimpleIcoText(Ico.PLAYER, "Participants:", Formatting.AQUA, 78); + this.addSimpleIcoText(Ico.SIGN, "Year:", Formatting.LIGHT_PURPLE, 79); + + } else { + // election is going on + this.addSimpleIcoText(Ico.CLOCK, "End in:", Formatting.GOLD, 76); + + for (int i = 77; i <= 79; i++) { + Matcher m = PlayerListMgr.regexAt(i, VOTE_PATTERN); + if (m == null) { + this.addComponent(new ProgressComponent()); + } else { + + String mayorname = m.group("mayor"); + String pcntstr = m.group("pcnt"); + float pcnt = Float.parseFloat(pcntstr); + Text candidate = Text.literal(mayorname).formatted(COLS[i - 77]); + ProgressComponent pc = new ProgressComponent(MAYOR_DATA.get(mayorname), candidate, pcnt, + COLS[i - 77].getColorValue()); + this.addComponent(pc); + } + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java new file mode 100644 index 00000000..85019dbf --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ErrorWidget.java @@ -0,0 +1,32 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// empty widget for when nothing can be shown + +public class ErrorWidget extends Widget { + private static final MutableText TITLE = Text.literal("Error").formatted(Formatting.RED, + Formatting.BOLD); + + Text error = Text.of("No info available!"); + + public ErrorWidget() { + super(TITLE, Formatting.RED.getColorValue()); + } + + public ErrorWidget(String error) { + super(TITLE, Formatting.RED.getColorValue()); + this.error = Text.of(error); + } + + @Override + public void updateContent() { + PlainTextComponent inf = new PlainTextComponent(this.error); + this.addComponent(inf); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java new file mode 100644 index 00000000..d171b753 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EssenceWidget.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows your dungeon essences (dungeon hub only) + +public class EssenceWidget extends Widget { + + private Text undead, wither, diamond, gold, dragon, spider, ice, crimson; + + private static final MutableText TITLE = Text.literal("Essences").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public EssenceWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + wither = Widget.simpleEntryText(46, "Wither:", Formatting.DARK_PURPLE); + spider = Widget.simpleEntryText(47, "Spider:", Formatting.DARK_PURPLE); + undead = Widget.simpleEntryText(48, "Undead:", Formatting.DARK_PURPLE); + dragon = Widget.simpleEntryText(49, "Dragon:", Formatting.DARK_PURPLE); + gold = Widget.simpleEntryText(50, "Gold:", Formatting.DARK_PURPLE); + diamond = Widget.simpleEntryText(51, "Diamond:", Formatting.DARK_PURPLE); + ice = Widget.simpleEntryText(52, "Ice:", Formatting.DARK_PURPLE); + crimson = Widget.simpleEntryText(53, "Crimson:", Formatting.DARK_PURPLE); + + TableComponent tc = new TableComponent(2, 4, Formatting.DARK_AQUA.getColorValue()); + + tc.addToCell(0, 0, new IcoTextComponent(Ico.WITHER, wither)); + tc.addToCell(0, 1, new IcoTextComponent(Ico.STRING, spider)); + tc.addToCell(0, 2, new IcoTextComponent(Ico.FLESH, undead)); + tc.addToCell(0, 3, new IcoTextComponent(Ico.DRAGON, dragon)); + tc.addToCell(1, 0, new IcoTextComponent(Ico.GOLD, gold)); + tc.addToCell(1, 1, new IcoTextComponent(Ico.DIAMOND, diamond)); + tc.addToCell(1, 2, new IcoTextComponent(Ico.ICE, ice)); + tc.addToCell(1, 3, new IcoTextComponent(Ico.REDSTONE, crimson)); + this.addComponent(tc); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java new file mode 100644 index 00000000..5a1e4239 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/EventWidget.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about ongoing events (e.g. election) + +public class EventWidget extends Widget { + private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD); + + private final boolean isInGarden; + + public EventWidget(boolean isInGarden) { + super(TITLE, Formatting.YELLOW.getColorValue()); + this.isInGarden = isInGarden; + } + + @Override + public void updateContent() { + // hypixel devs carefully inserting the most random edge cases #317: + // the event info is placed a bit differently when in the garden. + int offset = (isInGarden) ? -1 : 0; + + this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.YELLOW, 73 + offset); + + // this could look better + Text time = Widget.plainEntryText(74 + offset); + IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time); + this.addComponent(t); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java new file mode 100644 index 00000000..0211cbd6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java @@ -0,0 +1,68 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.Formatting; + +// this widget shows info about fire sales when in the hub. +// or not, if there isn't one going on + +public class FireSaleWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Fire Sale").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + // matches a fire sale item + // group 1: item name + // group 2: # items available + // group 3: # items available in total (1 digit + "k") + private static final Pattern FIRE_PATTERN = Pattern.compile("(?.*): (?\\d*)/(?[0-9.]*)k"); + + public FireSaleWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + String event = PlayerListMgr.strAt(46); + + if (event == null) { + this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY))); + return; + } + + if (event.contains("Starts In")) { + this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.DARK_AQUA, 46); + return; + } + + for (int i = 46;; i++) { + Matcher m = PlayerListMgr.regexAt( i, FIRE_PATTERN); + if (m == null) { + break; + } + String avail = m.group("avail"); + Text itemTxt = Text.literal(m.group("item")); + float total = Float.parseFloat(m.group("total")) * 1000; + Text prgressTxt = Text.literal(String.format("%s/%.0f", avail, total)); + float pcnt = (Float.parseFloat(avail) / (total)) * 100f; + ProgressComponent pc = new ProgressComponent(Ico.GOLD, itemTxt, prgressTxt, pcnt, pcntToCol(pcnt)); + this.addComponent(pc); + } + + } + + private int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb( pcnt / 300f, 0.9f, 0.9f); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java new file mode 100644 index 00000000..1a4683f5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ForgeWidget.java @@ -0,0 +1,81 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows what you're forging right now. +// for locked slots, the unlock requirement is shown + +public class ForgeWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Forge Status").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public ForgeWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + int forgestart = 54; + // why is it forges and not looms >:( + String pos = PlayerListMgr.strAt(53); + if (pos == null) { + this.addComponent(new IcoTextComponent()); + return; + } + + if (!pos.startsWith("Forges")) { + forgestart += 2; + } + + for (int i = forgestart, slot = 1; i < forgestart + 5 && i < 60; i++, slot++) { + String fstr = PlayerListMgr.strAt(i); + if (fstr == null || fstr.length() < 3) { + if (i == forgestart) { + this.addComponent(new IcoTextComponent()); + } + break; + } + Component c; + Text l1, l2; + + switch (fstr.substring(3)) { + case "LOCKED" -> { + l1 = Text.literal("Locked").formatted(Formatting.RED); + l2 = switch (slot) { + case 3 -> Text.literal("Needs HotM 3").formatted(Formatting.GRAY); + case 4 -> Text.literal("Needs HotM 4").formatted(Formatting.GRAY); + case 5 -> Text.literal("Needs PotM 2").formatted(Formatting.GRAY); + default -> + Text.literal("This message should not appear").formatted(Formatting.RED, Formatting.BOLD); + }; + c = new IcoFatTextComponent(Ico.BARRIER, l1, l2); + } + case "EMPTY" -> { + l1 = Text.literal("Empty").formatted(Formatting.GRAY); + c = new IcoTextComponent(Ico.FURNACE, l1); + } + default -> { + String[] parts = fstr.split(": "); + if (parts.length != 2) { + c = new IcoFatTextComponent(); + } else { + l1 = Text.literal(parts[0].substring(3)).formatted(Formatting.YELLOW); + l2 = Text.literal("Done in: ").formatted(Formatting.GRAY).append(Text.literal(parts[1]).formatted(Formatting.WHITE)); + c = new IcoFatTextComponent(Ico.FIRE, l1, l2); + } + } + } + this.addComponent(c); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java new file mode 100644 index 00000000..221f8b08 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java @@ -0,0 +1,54 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the garden server + +public class GardenServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + // match the next visitor in the garden + // group 1: visitor name + private static final Pattern VISITOR_PATTERN = Pattern.compile("Next Visitor: (?.*)"); + + public GardenServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + this.addSimpleIcoText(Ico.COPPER, "Copper:", Formatting.GOLD, 44); + + Matcher m = PlayerListMgr.regexAt(45, VISITOR_PATTERN); + if (m == null ) { + this.addComponent(new IcoTextComponent()); + return; + } + + String vis = m.group("vis"); + Formatting col; + if (vis.equals("Not Unlocked!")) { + col = Formatting.RED; + } else { + col = Formatting.GREEN; + } + Text visitor = Widget.simpleEntryText(vis, "Next Visitor: ", col); + IcoTextComponent v = new IcoTextComponent(Ico.PLAYER, visitor); + this.addComponent(v); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java new file mode 100644 index 00000000..e7058fd6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java @@ -0,0 +1,80 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about your skills while in the garden + +public class GardenSkillsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW, + Formatting.BOLD); + + // match the skill entry + // group 1: skill name and level + // group 2: progress to next level (without "%") + private static final Pattern SKILL_PATTERN = Pattern + .compile("\\S*: (?[A-Za-z]* [0-9]*): (?\\S*)%"); + // same, but with leading space + private static final Pattern MS_PATTERN = Pattern.compile("\\S*: (?[A-Za-z]* [0-9]*): (?\\S*)%"); + + public GardenSkillsWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + } + + @Override + public void updateContent() { + ProgressComponent pc; + Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN); + if (m == null) { + pc = new ProgressComponent(); + } else { + + String strpcnt = m.group("progress"); + String skill = m.group("skill"); + + float pcnt = Float.parseFloat(strpcnt); + pc = new ProgressComponent(Ico.LANTERN, Text.of(skill), pcnt, + Formatting.GOLD.getColorValue()); + } + + this.addComponent(pc); + + Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE); + IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); + Text farmfort = Widget.simpleEntryText(68, "FFO", Formatting.GOLD); + IcoTextComponent ffo = new IcoTextComponent(Ico.HOE, farmfort); + + TableComponent tc = new TableComponent(2, 1, Formatting.YELLOW.getColorValue()); + tc.addToCell(0, 0, spd); + tc.addToCell(1, 0, ffo); + this.addComponent(tc); + + ProgressComponent pc2; + m = PlayerListMgr.regexAt(69, MS_PATTERN); + if (m == null) { + pc2 = new ProgressComponent(); + } else { + String strpcnt = m.group("progress"); + String skill = m.group("skill"); + + float pcnt = Float.parseFloat(strpcnt); + pc2 = new ProgressComponent(Ico.MILESTONE, Text.of(skill), pcnt, + Formatting.GREEN.getColorValue()); + + } + this.addComponent(pc2); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java new file mode 100644 index 00000000..cfbd6cd0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class GardenVisitorsWidget extends Widget { + private static final MutableText TITLE = Text.literal("Visitors").formatted(Formatting.DARK_GREEN, Formatting.BOLD); + + public GardenVisitorsWidget() { + super(TITLE, Formatting.DARK_GREEN.getColorValue()); + } + + @Override + public void updateContent() { + if (PlayerListMgr.textAt(54) == null) { + this.addComponent(new PlainTextComponent(Text.literal("No visitors!").formatted(Formatting.GRAY))); + return; + } + + for (int i = 54; i < 59; i++) { + String text = PlayerListMgr.strAt(i); + if (text != null) + this.addComponent(new PlainTextComponent(Text.literal(text))); + } + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java new file mode 100644 index 00000000..bbd97fb5 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the private island you're visiting + +public class GuestServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public GuestServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.SIGN, "Owner:", Formatting.GREEN, 43); + this.addSimpleIcoText(Ico.SIGN, "Status:", Formatting.BLUE, 44); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java new file mode 100644 index 00000000..b527dc78 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows a list of all people visiting the same private island as you + +public class IslandGuestsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Guests").formatted(Formatting.AQUA, + Formatting.BOLD); + + // matches a player entry, removing their level and the hand icon + // group 1: player name + private static final Pattern GUEST_PATTERN = Pattern.compile("\\[\\d*\\] (.*) \\[.\\]"); + + public IslandGuestsWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + } + + @Override + public void updateContent() { + for (int i = 21; i < 40; i++) { + String str = PlayerListMgr.strAt(i); + if (str == null) { + if (i == 21) { + this.addComponent(new PlainTextComponent(Text.literal("No Visitors!").formatted(Formatting.GRAY))); + } + break; + } + Matcher m = PlayerListMgr.regexAt( i, GUEST_PATTERN); + if (m == null) { + this.addComponent(new PlainTextComponent(Text.of("???"))); + } else { + this.addComponent(new PlainTextComponent(Text.of(m.group(1)))); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java new file mode 100644 index 00000000..cde1fa38 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java @@ -0,0 +1,66 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows a list of the owners of a home island while guesting + +public class IslandOwnersWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // matches an owner + // group 1: player name + // group 2: last seen, if owner not online + // ^(?.*) \((?.*)\)$|^\[\d*\] (?:\[[A-Za-z]+\] )?(?[A-Za-z0-9_]*)(?: .*)?$|^(?.*)$ + private static final Pattern OWNER_PATTERN = Pattern + .compile("^(?.*) \\((?.*)\\)$|^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?(?[A-Za-z0-9_]*)(?: .*)?$|^(?.*)$"); + + public IslandOwnersWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + + for (int i = 1; i < 20; i++) { + Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN); + if (m == null) { + break; + } + + String name, lastseen; + Formatting format; + if (m.group("nameA") != null) { + name = m.group("nameA"); + lastseen = m.group("lastseen"); + format = Formatting.GRAY; + } else if (m.group("nameB")!=null){ + name = m.group("nameB"); + lastseen = "Online"; + format = Formatting.WHITE; + } else { + name = m.group("nameC"); + lastseen = "Online"; + format = Formatting.WHITE; + } + + Text entry = Text.literal(name) + .append( + Text.literal(" (" + lastseen + ")") + .formatted(format)); + PlainTextComponent ptc = new PlainTextComponent(entry); + this.addComponent(ptc); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java new file mode 100644 index 00000000..31ad66f7 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows a list of the owners while on your home island + +public class IslandSelfWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE, + Formatting.BOLD); + + // matches an owner + // group 1: player name, optionally offline time + // ^\[\d*\] (?:\[[A-Za-z]+\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$ + private static final Pattern OWNER_PATTERN = Pattern + .compile("^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$"); + + public IslandSelfWidget() { + super(TITLE, Formatting.DARK_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + for (int i = 1; i < 20; i++) { + Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN); + if (m == null) { + break; + } + + Text entry = (m.group(1) != null) ? Text.of(m.group(1)) : Text.of(m.group(2)); + this.addComponent(new PlainTextComponent(entry)); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java new file mode 100644 index 00000000..53dc11a6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java @@ -0,0 +1,32 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about your home island + +public class IslandServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public IslandServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Crystals:", Formatting.DARK_PURPLE, 43); + this.addSimpleIcoText(Ico.CHEST, "Stash:", Formatting.GREEN, 44); + this.addSimpleIcoText(Ico.COMMAND, "Minions:", Formatting.BLUE, 45); + + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java new file mode 100644 index 00000000..5ae0bd3d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java @@ -0,0 +1,62 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the current jacob's contest (garden only) + +public class JacobsContestWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Jacob's Contest").formatted(Formatting.YELLOW, + Formatting.BOLD); + + private static final HashMap FARM_DATA = new HashMap<>(); + + // again, there HAS to be a better way to do this + static { + FARM_DATA.put("Wheat", new ItemStack(Items.WHEAT)); + FARM_DATA.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); + FARM_DATA.put("Carrot", new ItemStack(Items.CARROT)); + FARM_DATA.put("Potato", new ItemStack(Items.POTATO)); + FARM_DATA.put("Melon", new ItemStack(Items.MELON_SLICE)); + FARM_DATA.put("Pumpkin", new ItemStack(Items.PUMPKIN)); + FARM_DATA.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); + FARM_DATA.put("Nether Wart", new ItemStack(Items.NETHER_WART)); + FARM_DATA.put("Cactus", new ItemStack(Items.CACTUS)); + FARM_DATA.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); + } + + public JacobsContestWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.GOLD, 76); + + TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue()); + + for (int i = 77; i < 80; i++) { + String item = PlayerListMgr.strAt(i); + IcoTextComponent itc; + if (item == null) { + itc = new IcoTextComponent(); + } else { + itc = new IcoTextComponent(FARM_DATA.get(item), Text.of(item)); + } + tc.addToCell(0, i - 77, itc); + } + this.addComponent(tc); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java new file mode 100644 index 00000000..35b9a0c6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/MinionWidget.java @@ -0,0 +1,151 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about minions placed on the home island + +public class MinionWidget extends Widget { + private static final MutableText TITLE = Text.literal("Minions").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + private static final HashMap MIN_ICOS = new HashMap<>(); + + // hmm... + static { + MIN_ICOS.put("Blaze", new ItemStack(Items.BLAZE_ROD)); + MIN_ICOS.put("Cave Spider", new ItemStack(Items.SPIDER_EYE)); + MIN_ICOS.put("Creeper", new ItemStack(Items.GUNPOWDER)); + MIN_ICOS.put("Enderman", new ItemStack(Items.ENDER_PEARL)); + MIN_ICOS.put("Ghast", new ItemStack(Items.GHAST_TEAR)); + MIN_ICOS.put("Magma Cube", new ItemStack(Items.MAGMA_CREAM)); + MIN_ICOS.put("Skeleton", new ItemStack(Items.BONE)); + MIN_ICOS.put("Slime", new ItemStack(Items.SLIME_BALL)); + MIN_ICOS.put("Spider", new ItemStack(Items.STRING)); + MIN_ICOS.put("Zombie", new ItemStack(Items.ROTTEN_FLESH)); + MIN_ICOS.put("Cactus", new ItemStack(Items.CACTUS)); + MIN_ICOS.put("Carrot", new ItemStack(Items.CARROT)); + MIN_ICOS.put("Chicken", new ItemStack(Items.CHICKEN)); + MIN_ICOS.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); + MIN_ICOS.put("Cow", new ItemStack(Items.BEEF)); + MIN_ICOS.put("Melon", new ItemStack(Items.MELON_SLICE)); + MIN_ICOS.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); + MIN_ICOS.put("Nether Wart", new ItemStack(Items.NETHER_WART)); + MIN_ICOS.put("Pig", new ItemStack(Items.PORKCHOP)); + MIN_ICOS.put("Potato", new ItemStack(Items.POTATO)); + MIN_ICOS.put("Pumpkin", new ItemStack(Items.PUMPKIN)); + MIN_ICOS.put("Rabbit", new ItemStack(Items.RABBIT)); + MIN_ICOS.put("Sheep", new ItemStack(Items.WHITE_WOOL)); + MIN_ICOS.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); + MIN_ICOS.put("Wheat", new ItemStack(Items.WHEAT)); + MIN_ICOS.put("Clay", new ItemStack(Items.CLAY)); + MIN_ICOS.put("Fishing", new ItemStack(Items.FISHING_ROD)); + MIN_ICOS.put("Coal", new ItemStack(Items.COAL)); + MIN_ICOS.put("Cobblestone", new ItemStack(Items.COBBLESTONE)); + MIN_ICOS.put("Diamond", new ItemStack(Items.DIAMOND)); + MIN_ICOS.put("Emerald", new ItemStack(Items.EMERALD)); + MIN_ICOS.put("End Stone", new ItemStack(Items.END_STONE)); + MIN_ICOS.put("Glowstone", new ItemStack(Items.GLOWSTONE_DUST)); + MIN_ICOS.put("Gold", new ItemStack(Items.GOLD_INGOT)); + MIN_ICOS.put("Gravel", new ItemStack(Items.GRAVEL)); + MIN_ICOS.put("Hard Stone", new ItemStack(Items.STONE)); + MIN_ICOS.put("Ice", new ItemStack(Items.ICE)); + MIN_ICOS.put("Iron", new ItemStack(Items.IRON_INGOT)); + MIN_ICOS.put("Lapis", new ItemStack(Items.LAPIS_LAZULI)); + MIN_ICOS.put("Mithril", new ItemStack(Items.PRISMARINE_CRYSTALS)); + MIN_ICOS.put("Mycelium", new ItemStack(Items.MYCELIUM)); + MIN_ICOS.put("Obsidian", new ItemStack(Items.OBSIDIAN)); + MIN_ICOS.put("Quartz", new ItemStack(Items.QUARTZ)); + MIN_ICOS.put("Red Sand", new ItemStack(Items.RED_SAND)); + MIN_ICOS.put("Redstone", new ItemStack(Items.REDSTONE)); + MIN_ICOS.put("Sand", new ItemStack(Items.SAND)); + MIN_ICOS.put("Snow", new ItemStack(Items.SNOWBALL)); + MIN_ICOS.put("Inferno", new ItemStack(Items.BLAZE_SPAWN_EGG)); + MIN_ICOS.put("Revenant", new ItemStack(Items.ZOMBIE_SPAWN_EGG)); + MIN_ICOS.put("Tarantula", new ItemStack(Items.SPIDER_SPAWN_EGG)); + MIN_ICOS.put("Vampire", new ItemStack(Items.REDSTONE)); + MIN_ICOS.put("Voidling", new ItemStack(Items.ENDERMAN_SPAWN_EGG)); + MIN_ICOS.put("Acacia", new ItemStack(Items.ACACIA_LOG)); + MIN_ICOS.put("Birch", new ItemStack(Items.BIRCH_LOG)); + MIN_ICOS.put("Dark Oak", new ItemStack(Items.DARK_OAK_LOG)); + MIN_ICOS.put("Flower", new ItemStack(Items.POPPY)); + MIN_ICOS.put("Jungle", new ItemStack(Items.JUNGLE_LOG)); + MIN_ICOS.put("Oak", new ItemStack(Items.OAK_LOG)); + MIN_ICOS.put("Spruce", new ItemStack(Items.SPRUCE_LOG)); + } + + // matches a minion entry + // group 1: name + // group 2: level + // group 3: status + public static final Pattern MINION_PATTERN = Pattern.compile("(?.*) (?[XVI]*) \\[(?.*)\\]"); + + public MinionWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + + // this looks a bit weird because if we used regex mismatch as a stop condition, + // it'd spam the chat. + // not sure if not having that debug output is worth the cleaner solution here... + + for (int i = 48; i < 59; i++) { + if (!this.addMinionComponent(i)) { + break; + } + } + + // if more minions are placed than the tab menu can display, + // a "And X more..." text is shown + // look for that and add it to the widget + String more = PlayerListMgr.strAt(59); + if (more == null) { + return; + } else if (more.startsWith("And ")) { + this.addComponent(new PlainTextComponent(Text.of(more))); + } else { + this.addMinionComponent(59); + } + } + + public boolean addMinionComponent(int i) { + Matcher m = PlayerListMgr.regexAt(i, MINION_PATTERN); + if (m != null) { + + String min = m.group("name"); + String lvl = m.group("level"); + String stat = m.group("status"); + + MutableText mt = Text.literal(min + " " + lvl).append(Text.literal(": ")); + + Formatting format = Formatting.RED; + if (stat.equals("ACTIVE")) { + format = Formatting.GREEN; + } else if (stat.equals("SLOW")) { + format = Formatting.YELLOW; + } + // makes "BLOCKED" also red. in reality, it's some kind of crimson + mt.append(Text.literal(stat).formatted(format)); + + IcoTextComponent itc = new IcoTextComponent(MIN_ICOS.get(min), mt); + this.addComponent(itc); + return true; + } else { + return false; + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java new file mode 100644 index 00000000..c781a1bc --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about the park server + +public class ParkServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public ParkServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + this.addSimpleIcoText(Ico.WATER, "Rain:", Formatting.BLUE, 44); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java new file mode 100644 index 00000000..ba178a5e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java @@ -0,0 +1,71 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlayerComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.ArrayList; +import java.util.Comparator; + +// this widget shows a list of players with their skins. +// responsible for non-private-island areas + +public class PlayerListWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Players").formatted(Formatting.GREEN, + Formatting.BOLD); + + public PlayerListWidget() { + super(TITLE, Formatting.GREEN.getColorValue()); + + } + + @Override + public void updateContent() { + ArrayList list = new ArrayList<>(); + + // hard cap to 4x20 entries. + // 5x20 is too wide (and not possible in theory. in reality however...) + int listlen = Math.min(PlayerListMgr.getSize(), 160); + + // list isn't fully loaded, so our hack won't work... + if (listlen < 80) { + this.addComponent(new PlainTextComponent(Text.literal("List loading...").formatted(Formatting.GRAY))); + return; + } + + // unintuitive int ceil division stolen from + // https://stackoverflow.com/questions/7139382/java-rounding-up-to-an-int-using-math-ceil#21830188 + int tblW = ((listlen - 80) - 1) / 20 + 1; + + TableComponent tc = new TableComponent(tblW, Math.min(listlen - 80, 20), Formatting.GREEN.getColorValue()); + + for (int i = 80; i < listlen; i++) { + list.add(PlayerListMgr.getRaw(i)); + } + + if (SkyblockerConfigManager.get().general.tabHud.nameSorting == SkyblockerConfig.NameSorting.ALPHABETICAL) { + list.sort(Comparator.comparing(o -> o.getProfile().getName().toLowerCase())); + } + + int x = 0, y = 0; + + for (PlayerListEntry ple : list) { + tc.addToCell(x, y, new PlayerComponent(ple)); + y++; + if (y >= 20) { + y = 0; + x++; + } + } + + this.addComponent(tc); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java new file mode 100644 index 00000000..44635fbe --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/PowderWidget.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows how much mithril and gemstone powder you have +// (dwarven mines and crystal hollows) + +public class PowderWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Powders").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public PowderWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MITHRIL, "Mithril:", Formatting.AQUA, 46); + this.addSimpleIcoText(Ico.EMERALD, "Gemstone:", Formatting.DARK_PURPLE, 47); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java new file mode 100644 index 00000000..de2ea0c6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ProfileWidget.java @@ -0,0 +1,28 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about your profile and bank + +public class ProfileWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.YELLOW, Formatting.BOLD); + + public ProfileWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61); + this.addSimpleIcoText(Ico.BONE, "Pet Sitter:", Formatting.AQUA, 62); + this.addSimpleIcoText(Ico.EMERALD, "Balance:", Formatting.GOLD, 63); + this.addSimpleIcoText(Ico.CLOCK, "Interest in:", Formatting.GOLD, 64); + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java new file mode 100644 index 00000000..3c3d3c92 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/QuestWidget.java @@ -0,0 +1,33 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows your crimson isle faction quests + +public class QuestWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Faction Quests").formatted(Formatting.AQUA, + Formatting.BOLD); + + public QuestWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + + } + + @Override + public void updateContent() { + for (int i = 51; i < 56; i++) { + Text q = PlayerListMgr.textAt(i); + IcoTextComponent itc = new IcoTextComponent(Ico.BOOK, q); + this.addComponent(itc); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java new file mode 100644 index 00000000..3c218fb1 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ReputationWidget.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows your faction status (crimson isle) + +public class ReputationWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Faction Status").formatted(Formatting.AQUA, + Formatting.BOLD); + + // matches your faction alignment progress + // group 1: percentage to next alignment level + private static final Pattern PROGRESS_PATTERN = Pattern.compile("\\|+ \\((?[0-9.]*)%\\)"); + + // matches alignment level names + // group 1: left level name + // group 2: right level name + private static final Pattern STATE_PATTERN = Pattern.compile("(?\\S*) *(?\\S*)"); + + public ReputationWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + } + + @Override + public void updateContent() { + String fracstr = PlayerListMgr.strAt(45); + + int spaceidx; + IcoTextComponent faction; + if (fracstr == null || (spaceidx = fracstr.indexOf(' ')) == -1) { + faction = new IcoTextComponent(); + } else { + String fname = fracstr.substring(0, spaceidx); + if (fname.equals("Mage")) { + faction = new IcoTextComponent(Ico.POTION, Text.literal(fname).formatted(Formatting.DARK_AQUA)); + } else { + faction = new IcoTextComponent(Ico.SWORD, Text.literal(fname).formatted(Formatting.RED)); + } + } + this.addComponent(faction); + + Text rep = Widget.plainEntryText(46); + Matcher prog = PlayerListMgr.regexAt(47, PROGRESS_PATTERN); + Matcher state = PlayerListMgr.regexAt(48, STATE_PATTERN); + + if (prog == null || state == null) { + this.addComponent(new ProgressComponent()); + } else { + float pcnt = Float.parseFloat(prog.group("prog")); + Text reputationText = state.group("from").equals("Max") ? Text.literal("Max Reputation") : Text.literal(state.group("from") + " -> " + state.group("to")); + ProgressComponent pc = new ProgressComponent(Ico.LANTERN, + reputationText, rep, pcnt, + Formatting.AQUA.getColorValue()); + this.addComponent(pc); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java new file mode 100644 index 00000000..475cb038 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ServerWidget.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about "generic" servers. +// a server is "generic", when only name, server ID and gems are shown +// in the third column of the tab HUD + +public class ServerWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public ServerWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java new file mode 100644 index 00000000..379fbb62 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SkillsWidget.java @@ -0,0 +1,78 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about a skill and some stats, +// as seen in the rightmost column of the default HUD + +public class SkillsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW, + Formatting.BOLD); + + // match the skill entry + // group 1: skill name and level + // group 2: progress to next level (without "%") + private static final Pattern SKILL_PATTERN = Pattern.compile("\\S*: ([A-Za-z]* [0-9]*): ([0-9.MAX]*)%?"); + + public SkillsWidget() { + super(TITLE, Formatting.YELLOW.getColorValue()); + + } + + @Override + public void updateContent() { + Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN); + Component progress; + if (m == null) { + progress = new ProgressComponent(); + } else { + + String skill = m.group(1); + String pcntStr = m.group(2); + + if (!pcntStr.equals("MAX")) { + float pcnt = Float.parseFloat(pcntStr); + progress = new ProgressComponent(Ico.LANTERN, Text.of(skill), + Text.of(pcntStr + "%"), pcnt, Formatting.GOLD.getColorValue()); + } else { + progress = new IcoFatTextComponent(Ico.LANTERN, Text.of(skill), + Text.literal(pcntStr).formatted(Formatting.RED)); + } + } + + this.addComponent(progress); + + Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE); + IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); + Text strength = Widget.simpleEntryText(68, "STR", Formatting.RED); + IcoTextComponent str = new IcoTextComponent(Ico.SWORD, strength); + Text critDmg = Widget.simpleEntryText(69, "CCH", Formatting.BLUE); + IcoTextComponent cdg = new IcoTextComponent(Ico.SWORD, critDmg); + Text critCh = Widget.simpleEntryText(70, "CDG", Formatting.BLUE); + IcoTextComponent cch = new IcoTextComponent(Ico.SWORD, critCh); + Text aSpeed = Widget.simpleEntryText(71, "ASP", Formatting.YELLOW); + IcoTextComponent asp = new IcoTextComponent(Ico.HOE, aSpeed); + + TableComponent tc = new TableComponent(2, 3, Formatting.YELLOW.getColorValue()); + tc.addToCell(0, 0, spd); + tc.addToCell(0, 1, str); + tc.addToCell(0, 2, asp); + tc.addToCell(1, 0, cdg); + tc.addToCell(1, 1, cch); + this.addComponent(tc); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java new file mode 100644 index 00000000..74bba632 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/TrapperWidget.java @@ -0,0 +1,25 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows how meny pelts you have (farming island) + +public class TrapperWidget extends Widget { + private static final MutableText TITLE = Text.literal("Trapper").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public TrapperWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.LEATHER, "Pelts:", Formatting.AQUA, 46); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java new file mode 100644 index 00000000..a245cbe9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java @@ -0,0 +1,51 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +// this widget shows info about ongoing profile/account upgrades +// or not, if there aren't any +// TODO: not very pretty atm + +public class UpgradeWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Upgrade Info").formatted(Formatting.GOLD, + Formatting.BOLD); + + public UpgradeWidget() { + super(TITLE, Formatting.GOLD.getColorValue()); + } + + @Override + public void updateContent() { + String footertext = PlayerListMgr.getFooter(); + + if (footertext == null) { + this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY))); + return; + } + + if (!footertext.contains("Upgrades")) { + this.addComponent(new PlainTextComponent(Text.of("Currently no upgrades..."))); + return; + } + + String interesting = footertext.split("Upgrades")[1]; + String[] lines = interesting.split("\n"); + + for (int i = 1; i < lines.length; i++) { + if (lines[i].trim().length() < 3) { // empty line is §s + break; + } + IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, Text.of(lines[i])); + this.addComponent(itc); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java new file mode 100644 index 00000000..8dacfb3a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java @@ -0,0 +1,59 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.HashMap; + +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Pair; + +// shows the volcano status (crimson isle) + +public class VolcanoWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Volcano Status").formatted(Formatting.AQUA, + Formatting.BOLD); + + private static final HashMap> BOOM_TYPE = new HashMap<>(); + + static { + BOOM_TYPE.put("INACTIVE", + new Pair<>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY)); + BOOM_TYPE.put("CHILL", + new Pair<>(new ItemStack(Items.ICE), Formatting.AQUA)); + BOOM_TYPE.put("LOW", + new Pair<>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY)); + BOOM_TYPE.put("DISRUPTIVE", + new Pair<>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE)); + BOOM_TYPE.put("MEDIUM", + new Pair<>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW)); + BOOM_TYPE.put("HIGH", + new Pair<>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD)); + BOOM_TYPE.put("EXPLOSIVE", + new Pair<>(new ItemStack(Items.TNT), Formatting.RED)); + BOOM_TYPE.put("CATACLYSMIC", + new Pair<>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED)); + } + + public VolcanoWidget() { + super(TITLE, Formatting.AQUA.getColorValue()); + + } + + @Override + public void updateContent() { + String s = PlayerListMgr.strAt(58); + if (s == null) { + this.addComponent(new IcoTextComponent()); + } else { + Pair p = BOOM_TYPE.get(s); + this.addComponent(new IcoTextComponent(p.getLeft(), Text.literal(s).formatted(p.getRight()))); + } + + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java new file mode 100644 index 00000000..5f0d2c3c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/Widget.java @@ -0,0 +1,216 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget; + +import java.util.ArrayList; + +import com.mojang.blaze3d.systems.RenderSystem; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Abstract base class for a Widget. + * Widgets are containers for components with a border and a title. + * Their size is dependent on the components inside, + * the position may be changed after construction. + */ +public abstract class Widget { + + private final ArrayList components = new ArrayList<>(); + private int w = 0, h = 0; + private int x = 0, y = 0; + private final int color; + private final Text title; + + private static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer; + + static final int BORDER_SZE_N = txtRend.fontHeight + 4; + static final int BORDER_SZE_S = 4; + static final int BORDER_SZE_W = 4; + static final int BORDER_SZE_E = 4; + static final int COL_BG_BOX = 0xc00c0c0c; + + public Widget(MutableText title, Integer colorValue) { + this.title = title; + this.color = 0xff000000 | colorValue; + } + + public final void addComponent(Component c) { + this.components.add(c); + } + + public final void update() { + this.components.clear(); + this.updateContent(); + this.pack(); + } + + public abstract void updateContent(); + + /** + * Shorthand function for simple components. + * If the entry at idx has the format ": ", an IcoTextComponent is + * added as such: + * [ico] [string] [textB.formatted(fmt)] + */ + public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, int idx) { + Text txt = Widget.simpleEntryText(idx, string, fmt); + this.addComponent(new IcoTextComponent(ico, txt)); + } + + /** + * Calculate the size of this widget. + * Must be called before returning from the widget constructor and after all + * components are added! + */ + private void pack() { + h = 0; + w = 0; + for (Component c : components) { + h += c.getHeight() + Component.PAD_L; + w = Math.max(w, c.getWidth() + Component.PAD_S); + } + + h -= Component.PAD_L / 2; // less padding after lowest/last component + h += BORDER_SZE_N + BORDER_SZE_S - 2; + w += BORDER_SZE_E + BORDER_SZE_W; + + // min width is dependent on title + w = Math.max(w, BORDER_SZE_W + BORDER_SZE_E + Widget.txtRend.getWidth(title) + 4 + 4 + 1); + } + + public final void setX(int x) { + this.x = x; + } + + public final int getY() { + return this.y; + } + + public final int getX() { + return this.x; + } + + public final void setY(int y) { + this.y = y; + } + + public final int getWidth() { + return this.w; + } + + public final int getHeight() { + return this.h; + } + + /** + * Draw this widget with a background + */ + public final void render(DrawContext context) { + this.render(context, true); + } + + /** + * Draw this widget, possibly with a background + */ + public final void render(DrawContext context, boolean hasBG) { + MatrixStack ms = context.getMatrices(); + + // not sure if this is the way to go, but it fixes Z-layer issues + // like blocks being rendered behind the BG and the hotbar clipping into things + RenderSystem.enableDepthTest(); + ms.push(); + + float scale = SkyblockerConfigManager.get().general.tabHud.tabHudScale / 100f; + ms.scale(scale, scale, 1); + + // move above other UI elements + ms.translate(0, 0, 200); + if (hasBG) { + context.fill(x + 1, y, x + w - 1, y + h, COL_BG_BOX); + context.fill(x, y + 1, x + 1, y + h - 1, COL_BG_BOX); + context.fill(x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX); + } + // move above background (if exists) + ms.translate(0, 0, 100); + + int strHeightHalf = Widget.txtRend.fontHeight / 2; + int strAreaWidth = Widget.txtRend.getWidth(title) + 4; + + context.drawText(txtRend, title, x + 8, y + 2, this.color, false); + + this.drawHLine(context, x + 2, y + 1 + strHeightHalf, 4); + this.drawHLine(context, x + 2 + strAreaWidth + 4, y + 1 + strHeightHalf, w - 4 - 4 - strAreaWidth); + this.drawHLine(context, x + 2, y + h - 2, w - 4); + + this.drawVLine(context, x + 1, y + 2 + strHeightHalf, h - 4 - strHeightHalf); + this.drawVLine(context, x + w - 2, y + 2 + strHeightHalf, h - 4 - strHeightHalf); + + int yOffs = y + BORDER_SZE_N; + + for (Component c : components) { + c.render(context, x + BORDER_SZE_W, yOffs); + yOffs += c.getHeight() + Component.PAD_L; + } + // pop manipulations above + ms.pop(); + RenderSystem.disableDepthTest(); + } + + private void drawHLine(DrawContext context, int xpos, int ypos, int width) { + context.fill(xpos, ypos, xpos + width, ypos + 1, this.color); + } + + private void drawVLine(DrawContext context, int xpos, int ypos, int height) { + context.fill(xpos, ypos, xpos + 1, ypos + height, this.color); + } + + /** + * If the entry at idx has the format "[textA]: [textB]", the following is + * returned: + * [entryName] [textB.formatted(contentFmt)] + */ + public static Text simpleEntryText(int idx, String entryName, Formatting contentFmt) { + + String src = PlayerListMgr.strAt(idx); + + if (src == null) { + return null; + } + + int cidx = src.indexOf(':'); + if (cidx == -1) { + return null; + } + + src = src.substring(src.indexOf(':') + 1); + return Widget.simpleEntryText(src, entryName, contentFmt); + } + + /** + * @return [entryName] [entryContent.formatted(contentFmt)] + */ + public static Text simpleEntryText(String entryContent, String entryName, Formatting contentFmt) { + return Text.literal(entryName).append(Text.literal(entryContent).formatted(contentFmt)); + } + + /** + * @return the entry at idx as unformatted Text + */ + public static Text plainEntryText(int idx) { + String str = PlayerListMgr.strAt(idx); + if (str == null) { + return null; + } + return Text.of(str); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java new file mode 100644 index 00000000..3c987068 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Component.java @@ -0,0 +1,31 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; + +/** + * Abstract base class for a component that may be added to a Widget. + */ +public abstract class Component { + + static final int ICO_DIM = 16; + public static final int PAD_S = 2; + public static final int PAD_L = 4; + + static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer; + + // these should always be the content dimensions without any padding. + int width, height; + + public abstract void render(DrawContext context, int x, int y); + + public int getWidth() { + return this.width; + } + + public int getHeight() { + return this.height; + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java new file mode 100644 index 00000000..def60d4d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java @@ -0,0 +1,45 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Component that consists of an icon and two lines of text + */ +public class IcoFatTextComponent extends Component { + + private static final int ICO_OFFS = 1; + + private ItemStack ico; + private Text line1, line2; + + public IcoFatTextComponent(ItemStack ico, Text l1, Text l2) { + this.ico = (ico == null) ? Ico.BARRIER : ico; + this.line1 = l1; + this.line2 = l2; + + if (l1 == null || l2 == null) { + this.ico = Ico.BARRIER; + this.line1 = Text.literal("No data").formatted(Formatting.GRAY); + this.line2 = Text.literal("No data").formatted(Formatting.GRAY); + } + + this.width = ICO_DIM + PAD_L + Math.max(txtRend.getWidth(this.line1), txtRend.getWidth(this.line2)); + this.height = txtRend.fontHeight + PAD_S + txtRend.fontHeight; + } + + public IcoFatTextComponent() { + this(null, null, null); + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawItem(ico, x, y + ICO_OFFS); + context.drawText(txtRend, line1, x + ICO_DIM + PAD_L, y, 0xffffffff, false); + context.drawText(txtRend, line2, x + ICO_DIM + PAD_L, y + txtRend.fontHeight + PAD_S, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java new file mode 100644 index 00000000..903a1fa3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Component that consists of an icon and a line of text. + */ +public class IcoTextComponent extends Component { + + private ItemStack ico; + private Text text; + + public IcoTextComponent(ItemStack ico, Text txt) { + this.ico = (ico == null) ? Ico.BARRIER : ico; + this.text = txt; + + if (txt == null) { + this.ico = Ico.BARRIER; + this.text = Text.literal("No data").formatted(Formatting.GRAY); + } + + this.width = ICO_DIM + PAD_L + txtRend.getWidth(this.text); + this.height = ICO_DIM; + } + + public IcoTextComponent() { + this(null, null); + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawItem(ico, x, y); + context.drawText(txtRend, text, x + ICO_DIM + PAD_L, y + 5, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java new file mode 100644 index 00000000..59e82e4e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import net.minecraft.client.gui.DrawContext; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Component that consists of a line of text. + */ +public class PlainTextComponent extends Component { + + private Text text; + + public PlainTextComponent(Text txt) { + this.text = txt; + + if (txt == null) { + this.text = Text.literal("No data").formatted(Formatting.GRAY); + } + + this.width = PAD_S + txtRend.getWidth(this.text); // looks off without padding + this.height = txtRend.fontHeight; + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawText(txtRend, text, x + PAD_S, y, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java new file mode 100644 index 00000000..ab79bb31 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java @@ -0,0 +1,39 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.PlayerSkinDrawer; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.scoreboard.Team; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +/** + * Component that consists of a player's skin icon and their name + */ +public class PlayerComponent extends Component { + + private static final int SKIN_ICO_DIM = 8; + + private final Text name; + private final Identifier tex; + + public PlayerComponent(PlayerListEntry ple) { + + boolean plainNames = SkyblockerConfigManager.get().general.tabHud.plainPlayerNames; + Team team = ple.getScoreboardTeam(); + String username = ple.getProfile().getName(); + name = (team != null && !plainNames) ? Text.empty().append(team.getPrefix()).append(Text.literal(username).formatted(team.getColor())).append(team.getSuffix()) : Text.of(username); + tex = ple.getSkinTextures().texture(); + + this.width = SKIN_ICO_DIM + PAD_S + txtRend.getWidth(name); + this.height = txtRend.fontHeight; + } + + @Override + public void render(DrawContext context, int x, int y) { + PlayerSkinDrawer.draw(context, tex, x, y, SKIN_ICO_DIM); + context.drawText(txtRend, name, x + SKIN_ICO_DIM + PAD_S, y, 0xffffffff, false); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java new file mode 100644 index 00000000..fa839dbe --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.item.ItemStack; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + + +/** + * Component that consists of an icon, some text and a progress bar. + * The progress bar either shows the fill percentage or custom text. + * NOTICE: pcnt is 0-100, not 0-1! + */ +public class ProgressComponent extends Component { + + private static final int BAR_WIDTH = 100; + private static final int BAR_HEIGHT = txtRend.fontHeight + 3; + private static final int ICO_OFFS = 4; + private static final int COL_BG_BAR = 0xf0101010; + + private final ItemStack ico; + private final Text desc, bar; + private final float pcnt; + private final int color; + private final int barW; + + public ProgressComponent(ItemStack ico, Text d, Text b, float pcnt, int color) { + if (d == null || b == null) { + this.ico = Ico.BARRIER; + this.desc = Text.literal("No data").formatted(Formatting.GRAY); + this.bar = Text.literal("---").formatted(Formatting.GRAY); + this.pcnt = 100f; + this.color = 0xff000000 | Formatting.DARK_GRAY.getColorValue(); + } else { + this.ico = (ico == null) ? Ico.BARRIER : ico; + this.desc = d; + this.bar = b; + this.pcnt = pcnt; + this.color = 0xff000000 | color; + } + + this.barW = BAR_WIDTH; + this.width = ICO_DIM + PAD_L + Math.max(this.barW, txtRend.getWidth(this.desc)); + this.height = txtRend.fontHeight + PAD_S + 2 + txtRend.fontHeight + 2; + } + + public ProgressComponent(ItemStack ico, Text text, float pcnt, int color) { + this(ico, text, Text.of(pcnt + "%"), pcnt, color); + } + + public ProgressComponent() { + this(null, null, null, 100, 0); + } + + @Override + public void render(DrawContext context, int x, int y) { + context.drawItem(ico, x, y + ICO_OFFS); + context.drawText(txtRend, desc, x + ICO_DIM + PAD_L, y, 0xffffffff, false); + + int barX = x + ICO_DIM + PAD_L; + int barY = y + txtRend.fontHeight + PAD_S; + int endOffsX = ((int) (this.barW * (this.pcnt / 100f))); + context.fill(barX + endOffsX, barY, barX + this.barW, barY + BAR_HEIGHT, COL_BG_BAR); + context.fill(barX, barY, barX + endOffsX, barY + BAR_HEIGHT, + this.color); + context.drawTextWithShadow(txtRend, bar, barX + 3, barY + 2, 0xffffffff); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java new file mode 100644 index 00000000..dbc0bf55 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/TableComponent.java @@ -0,0 +1,58 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.component; + +import net.minecraft.client.gui.DrawContext; + +/** + * Meta-Component that consists of a grid of other components + * Grid cols are separated by lines. + */ +public class TableComponent extends Component { + + private final Component[][] comps; + private final int color; + private final int cols, rows; + private int cellW, cellH; + + public TableComponent(int w, int h, int col) { + comps = new Component[w][h]; + color = 0xff000000 | col; + cols = w; + rows = h; + } + + public void addToCell(int x, int y, Component c) { + this.comps[x][y] = c; + + // pad extra to add a vertical line later + this.cellW = Math.max(this.cellW, c.width + PAD_S + PAD_L); + + // assume all rows are equally high so overwriting doesn't matter + // if this wasn't the case, drawing would need more math + // not doing any of that if it's not needed + this.cellH = c.height + PAD_S; + + this.width = this.cellW * this.cols; + this.height = (this.cellH * this.rows) - PAD_S / 2; + + } + + @Override + public void render(DrawContext context, int xpos, int ypos) { + for (int x = 0; x < cols; x++) { + for (int y = 0; y < rows; y++) { + if (comps[x][y] != null) { + comps[x][y].render(context, xpos + (x * cellW), ypos + y * cellH); + } + } + // add a line before the col if we're not drawing the first one + if (x != 0) { + int lineX1 = xpos + (x * cellW) - PAD_S - 1; + int lineX2 = xpos + (x * cellW) - PAD_S; + int lineY1 = ypos + 1; + int lineY2 = ypos + this.height - PAD_S - 1; // not sure why but it looks correct + context.fill(lineX1, lineY1, lineX2, lineY2, this.color); + } + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java new file mode 100644 index 00000000..6aa363c9 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java @@ -0,0 +1,73 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.hud; + +import java.util.List; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud.Commission; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +// this widget shows the status of the king's commissions. +// (dwarven mines and crystal hollows) +// USE ONLY WITH THE DWARVEN HUD! + +public class HudCommsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + private List commissions; + private boolean isFancy; + + // disgusting hack to get around text renderer issues. + // the ctor eventually tries to get the font's height, which doesn't work + // when called before the client window is created (roughly). + // the rebdering god 2 from the fabricord explained that detail, thanks! + public static final HudCommsWidget INSTANCE = new HudCommsWidget(); + public static final HudCommsWidget INSTANCE_CFG = new HudCommsWidget(); + + // another repulsive hack to make this widget-like hud element work with the new widget class + // DON'T USE WITH THE WIDGET SYSTEM, ONLY USE FOR DWARVENHUD! + public HudCommsWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + public void updateData(List commissions, boolean isFancy) { + this.commissions = commissions; + this.isFancy = isFancy; + } + + @Override + public void updateContent() { + for (Commission comm : commissions) { + + Text c = Text.literal(comm.commission()); + + float p = 100f; + if (!comm.progression().contains("DONE")) { + p = Float.parseFloat(comm.progression().substring(0, comm.progression().length() - 1)); + } + + Component comp; + if (isFancy) { + comp = new ProgressComponent(Ico.BOOK, c, p, pcntToCol(p)); + } else { + comp = new PlainTextComponent( + Text.literal(comm.commission() + ": ") + .append(Text.literal(comm.progression()).formatted(Formatting.GREEN))); + } + this.addComponent(comp); + } + } + + private int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java new file mode 100644 index 00000000..3499ce39 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java @@ -0,0 +1,35 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class AdvertisementWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Advertisement").formatted(Formatting.DARK_AQUA, + Formatting.BOLD); + + public AdvertisementWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + boolean added = false; + for (int i = 73; i < 80; i++) { + Text text = PlayerListMgr.textAt(i); + if (text != null) { + this.addComponent(new PlainTextComponent(text)); + added = true; + } + } + + if (!added) { + this.addComponent(new PlainTextComponent(Text.literal("No Advertisements").formatted(Formatting.GRAY))); + } + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java new file mode 100644 index 00000000..3a5da142 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java @@ -0,0 +1,69 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class GoodToKnowWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Good To Know").formatted(Formatting.BLUE, Formatting.BOLD); + + public GoodToKnowWidget() { + super(TITLE, Formatting.BLUE.getColorValue()); + } + + @Override + public void updateContent() { + // After you progress further the tab adds more info so we need to be careful of + // that + // In beginning it only shows montezuma, then timecharms and enigma souls are + // added + + int headerPos = 0; + // this seems suboptimal, but I'm not sure if there's a way to do it better. + // search for the GTK header and offset the rest accordingly. + for (int i = 45; i <= 49; i++) { + String str = PlayerListMgr.strAt(i); + if (str != null && str.startsWith("Good to")) { + headerPos = i; + break; + } + } + + Text posA = PlayerListMgr.textAt(headerPos + 2); // Can be times visited rift + Text posB = PlayerListMgr.textAt(headerPos + 4); // Can be lifetime motes or visited rift + Text posC = PlayerListMgr.textAt(headerPos + 6); // Can be lifetime motes + + int visitedRiftPos = 0; + int lifetimeMotesPos = 0; + + // Check each position to see what is or isn't there so we don't try adding + // invalid components + if (posA != null && posA.getString().contains("times")) + visitedRiftPos = headerPos + 2; + if (posB != null && posB.getString().contains("Motes")) + lifetimeMotesPos = headerPos + 4; + if (posB != null && posB.getString().contains("times")) + visitedRiftPos = headerPos + 4; + if (posC != null && posC.getString().contains("Motes")) + lifetimeMotesPos = headerPos + 6; + + Text timesVisitedRift = (visitedRiftPos == headerPos + 4) ? posB : (visitedRiftPos == headerPos + 2) ? posA : Text.literal("No Data").formatted(Formatting.GRAY); + Text lifetimeMotesEarned = (lifetimeMotesPos == headerPos + 6) ? posC : (lifetimeMotesPos == headerPos + 4) ? posB : Text.literal("No Data").formatted(Formatting.GRAY); + + if (visitedRiftPos != 0) { + this.addComponent(new IcoTextComponent(Ico.EXPERIENCE_BOTTLE, + Text.literal("Visited Rift: ").append(timesVisitedRift))); + } + + if (lifetimeMotesPos != 0) { + this.addComponent( + new IcoTextComponent(Ico.PINK_DYE, Text.literal("Lifetime Earned: ").append(lifetimeMotesEarned))); + } + + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java new file mode 100644 index 00000000..178bf142 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class RiftProfileWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + public RiftProfileWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java new file mode 100644 index 00000000..93ade5cb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java @@ -0,0 +1,123 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.math.MathHelper; + +public class RiftProgressWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Rift Progress").formatted(Formatting.BLUE, Formatting.BOLD); + + private static final Pattern TIMECHARMS_PATTERN = Pattern.compile("Timecharms: (?[0-9]+)\\/(?[0-9]+)"); + private static final Pattern ENIGMA_SOULS_PATTERN = Pattern.compile("Enigma Souls: (?[0-9]+)\\/(?[0-9]+)"); + private static final Pattern MONTEZUMA_PATTERN = Pattern.compile("Montezuma: (?[0-9]+)\\/(?[0-9]+)"); + + public RiftProgressWidget() { + super(TITLE, Formatting.BLUE.getColorValue()); + } + + @Override + public void updateContent() { + // After you progress further, the tab adds more info so we need to be careful + // of that. + // In beginning it only shows montezuma, then timecharms and enigma souls are + // added. + + String pos44 = PlayerListMgr.strAt(44); + + // LHS short-circuits, so the RHS won't be evaluated on pos44 == null + if (pos44 == null || !pos44.contains("Rift Progress")) { + this.addComponent(new PlainTextComponent(Text.literal("No Progress").formatted(Formatting.GRAY))); + return; + } + + // let's try to be clever by assuming what progress item may appear where and + // when to skip testing every slot for every thing. + + // always non-null, as this holds the topmost item. + // if there is none, there shouldn't be a header. + String pos45 = PlayerListMgr.strAt(45); + + // Can be Montezuma, Enigma Souls or Timecharms. + // assume timecharms can only appear here and that they're the last thing to + // appear, so if this exists, we know the rest. + if (pos45.contains("Timecharms")) { + addTimecharmsComponent(45); + addEnigmaSoulsComponent(46); + addMontezumaComponent(47); + return; + } + + // timecharms didn't appear at the top, so there's two or one entries. + // assume that if there's two, souls is always top. + String pos46 = PlayerListMgr.strAt(46); + + if (pos45.contains("Enigma Souls")) { + addEnigmaSoulsComponent(45); + if (pos46 != null) { + // souls might appear alone. + // if there's a second entry, it has to be montezuma + addMontezumaComponent(46); + } + } else { + // first entry isn't souls, so it's just montezuma and nothing else. + addMontezumaComponent(45); + } + + } + + private static int pcntToCol(float pcnt) { + return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); + } + + private void addTimecharmsComponent(int pos) { + Matcher m = PlayerListMgr.regexAt(pos, TIMECHARMS_PATTERN); + + int current = Integer.parseInt(m.group("current")); + int total = Integer.parseInt(m.group("total")); + float pcnt = ((float) current / (float) total) * 100f; + Text progressText = Text.literal(current + "/" + total); + + ProgressComponent pc = new ProgressComponent(Ico.NETHER_STAR, Text.literal("Timecharms"), progressText, + pcnt, pcntToCol(pcnt)); + + this.addComponent(pc); + } + + private void addEnigmaSoulsComponent(int pos) { + Matcher m = PlayerListMgr.regexAt(pos, ENIGMA_SOULS_PATTERN); + + int current = Integer.parseInt(m.group("current")); + int total = Integer.parseInt(m.group("total")); + float pcnt = ((float) current / (float) total) * 100f; + Text progressText = Text.literal(current + "/" + total); + + ProgressComponent pc = new ProgressComponent(Ico.HEART_OF_THE_SEA, Text.literal("Enigma Souls"), + progressText, pcnt, pcntToCol(pcnt)); + + this.addComponent(pc); + } + + private void addMontezumaComponent(int pos) { + Matcher m = PlayerListMgr.regexAt(pos, MONTEZUMA_PATTERN); + + int current = Integer.parseInt(m.group("current")); + int total = Integer.parseInt(m.group("total")); + float pcnt = ((float) current / (float) total) * 100f; + Text progressText = Text.literal(current + "/" + total); + + ProgressComponent pc = new ProgressComponent(Ico.BONE, Text.literal("Montezuma"), progressText, pcnt, + pcntToCol(pcnt)); + + this.addComponent(pc); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java new file mode 100644 index 00000000..89530e2f --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Special version of the server info widget for the rift! + * + */ +public class RiftServerInfoWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD); + + public RiftServerInfoWidget() { + super(TITLE, Formatting.LIGHT_PURPLE.getColorValue()); + } + + @Override + public void updateContent() { + this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.LIGHT_PURPLE, 41); + this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); + } + +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java new file mode 100644 index 00000000..0e2f323d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.TableComponent; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class RiftStatsWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Stats").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + public RiftStatsWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + Text riftDamage = Widget.simpleEntryText(64, "RDG", Formatting.DARK_PURPLE); + IcoTextComponent rdg = new IcoTextComponent(Ico.DIASWORD, riftDamage); + + Text speed = Widget.simpleEntryText(65, "SPD", Formatting.WHITE); + IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); + + Text intelligence = Widget.simpleEntryText(66, "INT", Formatting.AQUA); + IcoTextComponent intel = new IcoTextComponent(Ico.ENCHANTED_BOOK, intelligence); + + Text manaRegen = Widget.simpleEntryText(67, "MRG", Formatting.AQUA); + IcoTextComponent mrg = new IcoTextComponent(Ico.DIAMOND, manaRegen); + + TableComponent tc = new TableComponent(2, 2, Formatting.AQUA.getColorValue()); + tc.addToCell(0, 0, rdg); + tc.addToCell(0, 1, spd); + tc.addToCell(1, 0, intel); + tc.addToCell(1, 1, mrg); + + this.addComponent(tc); + } + +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java new file mode 100644 index 00000000..2827400e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.skyblock.tabhud.widget.rift; + +import de.hysky.skyblocker.skyblock.tabhud.widget.Widget; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class ShenWidget extends Widget { + + private static final MutableText TITLE = Text.literal("Shen's Countdown").formatted(Formatting.DARK_AQUA, Formatting.BOLD); + + public ShenWidget() { + super(TITLE, Formatting.DARK_AQUA.getColorValue()); + } + + @Override + public void updateContent() { + this.addComponent(new PlainTextComponent(Text.literal(PlayerListMgr.strAt(70)))); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Boxes.java b/src/main/java/de/hysky/skyblocker/utils/Boxes.java new file mode 100644 index 00000000..cd944a46 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Boxes.java @@ -0,0 +1,50 @@ +package de.hysky.skyblocker.utils; + +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction.Axis; +import net.minecraft.util.math.Vec3d; + +public class Boxes { + /** Returns the vector of the min pos of this box. **/ + public static Vec3d getMinVec(Box box) { + return new Vec3d(box.minX, box.minY, box.minZ); + } + + /** Returns the vector of the max pos of this box. **/ + public static Vec3d getMaxVec(Box box) { + return new Vec3d(box.maxX, box.maxY, box.maxZ); + } + + /** Returns the vector of the side lengths of this box. **/ + public static Vec3d getLengthVec(Box box) { + return new Vec3d(box.getLengthX(), box.getLengthY(), box.getLengthZ()); + } + + /** Offsets this box so that minX, minY and minZ are all zero. **/ + public static Box moveToZero(Box box) { + return box.offset(getMinVec(box).negate()); + } + + /** Returns the distance between to oppisite corners of the box. **/ + public static double getCornerLength(Box box) { + return getMinVec(box).distanceTo(getMaxVec(box)); + } + + /** Returns the length of an axis in the box. **/ + public static double getAxisLength(Box box, Axis axis) { + return box.getMax(axis) - box.getMin(axis); + } + + /** Returns a box with each axis multiplied by the amount specified. **/ + public static Box multiply(Box box, double amount) { + return multiply(box, amount, amount, amount); + } + + /** Returns a box with each axis multiplied by the amount specified. **/ + public static Box multiply(Box box, double x, double y, double z) { + return box.expand( + getAxisLength(box, Axis.X) * (x - 1) / 2d, + getAxisLength(box, Axis.Y) * (y - 1) / 2d, + getAxisLength(box, Axis.Z) * (z - 1) / 2d); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Constants.java b/src/main/java/de/hysky/skyblocker/utils/Constants.java new file mode 100644 index 00000000..fbeb448c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Constants.java @@ -0,0 +1,8 @@ +package de.hysky.skyblocker.utils; + +/** + * Holds generic static constants + */ +public interface Constants { + String LEVEL_EMBLEMS = "\u2E15\u273F\u2741\u2E19\u03B1\u270E\u2615\u2616\u2663\u213B\u2694\u27B6\u26A1\u2604\u269A\u2693\u2620\u269B\u2666\u2660\u2764\u2727\u238A\u1360\u262C\u269D\u29C9\uA214\u32D6\u2E0E\u26A0\uA541\u3020\u30C4\u2948\u2622\u2623\u273E\u269C\u0BD0\u0A6D\u2742\u16C3\u3023\u10F6\u0444\u266A\u266B\u04C3\u26C1\u26C3\u16DD\uA03E\u1C6A\u03A3\u09EB\u2603\u2654\u26C2\u12DE"; +} diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java new file mode 100644 index 00000000..ee500b5a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Http.java @@ -0,0 +1,89 @@ +package de.hysky.skyblocker.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpClient.Version; +import java.net.http.HttpHeaders; +import java.net.http.HttpRequest; +import java.net.http.HttpRequest.BodyPublishers; +import java.net.http.HttpResponse; +import java.net.http.HttpResponse.BodyHandlers; +import java.time.Duration; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; + +import de.hysky.skyblocker.SkyblockerMod; +import net.minecraft.SharedConstants; + +/** + * @implNote All http requests are sent using HTTP 2 + */ +public class Http { + private static final String USER_AGENT = "Skyblocker/" + SkyblockerMod.VERSION + " (" + SharedConstants.getGameVersion().getName() + ")"; + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(10)) + .build(); + + public static String sendGetRequest(String url) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .GET() + .header("Accept", "application/json") + .header("Accept-Encoding", "gzip, deflate") + .header("User-Agent", USER_AGENT) + .version(Version.HTTP_2) + .uri(URI.create(url)) + .build(); + + HttpResponse response = HTTP_CLIENT.send(request, BodyHandlers.ofInputStream()); + InputStream decodedInputStream = getDecodedInputStream(response); + String body = new String(decodedInputStream.readAllBytes()); + + return body; + } + + public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder() + .method("HEAD", BodyPublishers.noBody()) + .header("User-Agent", USER_AGENT) + .version(Version.HTTP_2) + .uri(URI.create(url)) + .build(); + + HttpResponse response = HTTP_CLIENT.send(request, BodyHandlers.discarding()); + return response.headers(); + } + + private static InputStream getDecodedInputStream(HttpResponse response) { + String encoding = getContentEncoding(response); + + try { + switch (encoding) { + case "": + return response.body(); + case "gzip": + return new GZIPInputStream(response.body()); + case "deflate": + return new InflaterInputStream(response.body()); + default: + throw new UnsupportedOperationException("The server sent content in an unexpected encoding: " + encoding); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private static String getContentEncoding(HttpResponse response) { + return response.headers().firstValue("Content-Encoding").orElse(""); + } + + public static String getEtag(HttpHeaders headers) { + return headers.firstValue("Etag").orElse(""); + } + + public static String getLastModified(HttpHeaders headers) { + return headers.firstValue("Last-Modified").orElse(""); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java new file mode 100644 index 00000000..6ae1b4d0 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -0,0 +1,111 @@ +package de.hysky.skyblocker.utils; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.item.TooltipContext; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.StringNbtReader; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.regex.Pattern; + +public class ItemUtils { + private final static Pattern WHITESPACES = Pattern.compile("^\\s*$"); + + public static List getTooltip(ItemStack item) { + MinecraftClient client = MinecraftClient.getInstance(); + return client.player == null || item == null ? Collections.emptyList() : item.getTooltip(client.player, TooltipContext.Default.BASIC); + } + + public static List getTooltipStrings(ItemStack item) { + List lines = getTooltip(item); + List list = new ArrayList<>(); + + for (Text line : lines) { + String string = line.getString(); + if (!WHITESPACES.matcher(string).matches()) + list.add(string); + } + + return list; + } + + @Nullable + public static Durability getDurability(ItemStack stack) { + if (!Utils.isOnSkyblock() || !SkyblockerConfigManager.get().locations.dwarvenMines.enableDrillFuel || stack.isEmpty()) { + return null; + } + + NbtCompound tag = stack.getNbt(); + if (tag == null || !tag.contains("ExtraAttributes")) { + return null; + } + + NbtCompound extraAttributes = tag.getCompound("ExtraAttributes"); + if (!extraAttributes.contains("drill_fuel") && !extraAttributes.getString("id").equals("PICKONIMBUS")) { + return null; + } + + int current = 0; + int max = 0; + String clearFormatting; + + for (String line : ItemUtils.getTooltipStrings(stack)) { + clearFormatting = Formatting.strip(line); + if (line.contains("Fuel: ")) { + if (clearFormatting != null) { + String clear = Pattern.compile("[^0-9 /]").matcher(clearFormatting).replaceAll("").trim(); + String[] split = clear.split("/"); + current = Integer.parseInt(split[0]); + max = Integer.parseInt(split[1]) * 1000; + return new Durability(current, max); + } + } else if (line.contains("uses.")) { + if (clearFormatting != null) { + int startIndex = clearFormatting.lastIndexOf("after") + 6; + int endIndex = clearFormatting.indexOf("uses", startIndex); + if (startIndex >= 0 && endIndex > startIndex) { + String usesString = clearFormatting.substring(startIndex, endIndex).trim(); + current = Integer.parseInt(usesString); + max = 5000; + } + return new Durability(current, max); + } + } + } + + return null; + } + + public static ItemStack getSkyblockerStack() { + try { + return ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}")); + } catch (CommandSyntaxException e) { + throw new RuntimeException(e); + } + } + + public static String getItemId(ItemStack itemStack) { + if (itemStack == null) return null; + + NbtCompound nbt = itemStack.getNbt(); + if (nbt != null && nbt.contains("ExtraAttributes")) { + NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); + if (extraAttributes.contains("id")) { + return extraAttributes.getString("id"); + } + } + + return null; + } + + public record Durability(int current, int max) { + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/NEURepo.java b/src/main/java/de/hysky/skyblocker/utils/NEURepo.java new file mode 100644 index 00000000..9bc6b245 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/NEURepo.java @@ -0,0 +1,101 @@ +package de.hysky.skyblocker.utils; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.TransportException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Initializes the NEU repo, which contains item metadata and fairy souls location data. Clones the repo if it does not exist and checks for updates. Use {@link #runAsyncAfterLoad(Runnable)} to run code after the repo is initialized. + */ +public class NEURepo { + private static final Logger LOGGER = LoggerFactory.getLogger(NEURepo.class); + public static final String REMOTE_REPO_URL = "https://github.com/NotEnoughUpdates/NotEnoughUpdates-REPO.git"; + public static final Path LOCAL_REPO_DIR = SkyblockerMod.CONFIG_DIR.resolve("item-repo"); + private static final CompletableFuture REPO_INITIALIZED = initRepository(); + + /** + * Adds command to update repository manually from ingame. + *

    + * TODO A button could be added to the settings menu that will trigger this command. + */ + public static void init() { + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> + dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE) + .then(ClientCommandManager.literal("updaterepository").executes(context -> { + deleteAndDownloadRepository(); + return 1; + })))); + } + + private static CompletableFuture initRepository() { + return CompletableFuture.runAsync(() -> { + try { + if (Files.isDirectory(NEURepo.LOCAL_REPO_DIR)) { + try (Git localRepo = Git.open(NEURepo.LOCAL_REPO_DIR.toFile())) { + localRepo.pull().setRebase(true).call(); + LOGGER.info("[Skyblocker] NEU Repository Updated"); + } + } else { + Git.cloneRepository().setURI(REMOTE_REPO_URL).setDirectory(NEURepo.LOCAL_REPO_DIR.toFile()).setBranchesToClone(List.of("refs/heads/master")).setBranch("refs/heads/master").call().close(); + LOGGER.info("[Skyblocker] NEU Repository Downloaded"); + } + } catch (TransportException e){ + LOGGER.error("[Skyblocker] Transport operation failed. Most likely unable to connect to the remote NEU repo on github", e); + } catch (RepositoryNotFoundException e) { + LOGGER.warn("[Skyblocker] Local NEU Repository not found or corrupted, downloading new one", e); + deleteAndDownloadRepository(); + } catch (Exception e) { + LOGGER.error("[Skyblocker] Encountered unknown exception while initializing NEU Repository", e); + } + }); + } + + private static void deleteAndDownloadRepository() { + CompletableFuture.runAsync(() -> { + try { + ItemRegistry.filesImported = false; + File dir = NEURepo.LOCAL_REPO_DIR.toFile(); + recursiveDelete(dir); + } catch (Exception ex) { + if (MinecraftClient.getInstance().player != null) + MinecraftClient.getInstance().player.sendMessage(Text.translatable("skyblocker.updaterepository.failed"), false); + return; + } + initRepository(); + }); + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + private static void recursiveDelete(File dir) { + File[] children; + if (dir.isDirectory() && !Files.isSymbolicLink(dir.toPath()) && (children = dir.listFiles()) != null) { + for (File child : children) { + recursiveDelete(child); + } + } + dir.delete(); + } + + /** + * Runs the given runnable after the NEU repo is initialized. + * @param runnable the runnable to run + * @return a completable future of the given runnable + */ + public static CompletableFuture runAsyncAfterLoad(Runnable runnable) { + return REPO_INITIALIZED.thenRunAsync(runnable); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/PosUtils.java b/src/main/java/de/hysky/skyblocker/utils/PosUtils.java new file mode 100644 index 00000000..6a34b060 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/PosUtils.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.utils; + +import net.minecraft.util.math.BlockPos; + +public final class PosUtils { + public static BlockPos parsePosString(String posData) { + String[] posArray = posData.split(","); + return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2])); + } + + public static String getPosString(BlockPos blockPos) { + return blockPos.getX() + "," + blockPos.getY() + "," + blockPos.getZ(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java b/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java new file mode 100644 index 00000000..0a42c6ae --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/SlayerUtils.java @@ -0,0 +1,54 @@ +package de.hysky.skyblocker.utils; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +//TODO Slayer Packet system that can provide information about the current slayer boss, abstract so that different bosses can have different info +public class SlayerUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(SlayerUtils.class); + + //TODO: Cache this, probably included in Packet system + public static List getEntityArmorStands(Entity entity) { + return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(1F, 2.5F, 1F), x -> x instanceof ArmorStandEntity && x.hasCustomName()); + } + + //Eventually this should be modified so that if you hit a slayer boss all slayer features will work on that boss. + public static Entity getSlayerEntity() { + if (MinecraftClient.getInstance().world != null) { + for (Entity entity : MinecraftClient.getInstance().world.getEntities()) { + //Check if entity is Bloodfiend + if (entity.hasCustomName() && entity.getCustomName().getString().contains("Bloodfiend")) { + //Grab the players username + String username = MinecraftClient.getInstance().getSession().getUsername(); + //Check all armor stands around the boss + for (Entity armorStand : getEntityArmorStands(entity)) { + //Check if the display name contains the players username + if (armorStand.getDisplayName().getString().contains(username)) { + return entity; + } + } + } + } + } + return null; + } + + public static boolean isInSlayer() { + try { + for (int i = 0; i < Utils.STRING_SCOREBOARD.size(); i++) { + String line = Utils.STRING_SCOREBOARD.get(i); + + if (line.contains("Slay the boss!")) return true; + } + } catch (NullPointerException e) { + LOGGER.error("[Skyblocker] Error while checking if player is in slayer", e); + } + + return false; + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java new file mode 100644 index 00000000..f046bffb --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -0,0 +1,370 @@ +package de.hysky.skyblocker.utils; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; + +import de.hysky.skyblocker.events.SkyblockEvents; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip; +import de.hysky.skyblocker.skyblock.rift.TheRift; +import de.hysky.skyblocker.utils.scheduler.MessageScheduler; +import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayNetworkHandler; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.network.PlayerListEntry; +import net.minecraft.scoreboard.Scoreboard; +import net.minecraft.scoreboard.ScoreboardDisplaySlot; +import net.minecraft.scoreboard.ScoreboardObjective; +import net.minecraft.scoreboard.ScoreboardPlayerScore; +import net.minecraft.scoreboard.Team; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; + +/** + * Utility variables and methods for retrieving Skyblock related information. + */ +public class Utils { + private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); + private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", ""); + private static final String PROFILE_PREFIX = "Profile: "; + private static boolean isOnHypixel = false; + private static boolean isOnSkyblock = false; + private static boolean isInDungeons = false; + private static boolean isInjected = false; + /** + * The following fields store data returned from /locraw: {@link #profile}, {@link #server}, {@link #gameType}, {@link #locationRaw}, and {@link #map}. + */ + @SuppressWarnings("JavadocDeclaration") + private static String profile = ""; + private static String server = ""; + private static String gameType = ""; + private static String locationRaw = ""; + private static String map = ""; + private static long clientWorldJoinTime = 0; + private static boolean sentLocRaw = false; + private static boolean canSendLocRaw = false; + + /** + * @implNote The parent text will always be empty, the actual text content is inside the text's siblings. + */ + public static final ObjectArrayList TEXT_SCOREBOARD = new ObjectArrayList<>(); + public static final ObjectArrayList STRING_SCOREBOARD = new ObjectArrayList<>(); + + public static boolean isOnHypixel() { + return isOnHypixel; + } + + public static boolean isOnSkyblock() { + return isOnSkyblock; + } + + public static boolean isInDungeons() { + return isInDungeons; + } + + public static boolean isInTheRift() { + return getLocationRaw().equals(TheRift.LOCATION); + } + + public static boolean isInjected() { + return isInjected; + } + + /** + * @return the profile parsed from the player list. + */ + public static String getProfile() { + return profile; + } + + /** + * @return the server parsed from /locraw. + */ + public static String getServer() { + return server; + } + + /** + * @return the game type parsed from /locraw. + */ + public static String getGameType() { + return gameType; + } + + /** + * @return the location raw parsed from /locraw. + */ + public static String getLocationRaw() { + return locationRaw; + } + + /** + * @return the map parsed from /locraw. + */ + public static String getMap() { + return map; + } + + public static void init() { + ClientPlayConnectionEvents.JOIN.register(Utils::onClientWorldJoin); + ClientReceiveMessageEvents.ALLOW_GAME.register(Utils::onChatMessage); + ClientReceiveMessageEvents.GAME_CANCELED.register(Utils::onChatMessage); // Somehow this works even though onChatMessage returns a boolean + } + + /** + * Updates all the fields stored in this class from the sidebar, player list, and /locraw. + */ + public static void update() { + MinecraftClient client = MinecraftClient.getInstance(); + updateScoreboard(client); + updatePlayerPresenceFromScoreboard(client); + updateFromPlayerList(client); + updateLocRaw(); + } + + /** + * Updates {@link #isOnSkyblock}, {@link #isInDungeons}, and {@link #isInjected} from the scoreboard. + */ + public static void updatePlayerPresenceFromScoreboard(MinecraftClient client) { + List sidebar = STRING_SCOREBOARD; + + FabricLoader fabricLoader = FabricLoader.getInstance(); + if ((client.world == null || client.isInSingleplayer() || sidebar == null || sidebar.isEmpty())) { + if (fabricLoader.isDevelopmentEnvironment()) { + sidebar = Collections.emptyList(); + } else { + isOnSkyblock = false; + isInDungeons = false; + return; + } + } + + if (sidebar.isEmpty() && !fabricLoader.isDevelopmentEnvironment()) return; + String string = sidebar.toString(); + + if (fabricLoader.isDevelopmentEnvironment() || isConnectedToHypixel(client)) { + if (!isOnHypixel) { + isOnHypixel = true; + } + if (fabricLoader.isDevelopmentEnvironment() || sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) { + if (!isOnSkyblock) { + if (!isInjected) { + isInjected = true; + ItemTooltipCallback.EVENT.register(PriceInfoTooltip::onInjectTooltip); + } + isOnSkyblock = true; + SkyblockEvents.JOIN.invoker().onSkyblockJoin(); + } + } else { + onLeaveSkyblock(); + } + isInDungeons = fabricLoader.isDevelopmentEnvironment() || isOnSkyblock && string.contains("The Catacombs"); + } else if (isOnHypixel) { + isOnHypixel = false; + onLeaveSkyblock(); + } + } + + private static boolean isConnectedToHypixel(MinecraftClient client) { + String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : ""; + String serverBrand = (client.player != null && client.player.networkHandler != null && client.player.networkHandler.getBrand() != null) ? client.player.networkHandler.getBrand() : ""; + + return serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"); + } + + private static void onLeaveSkyblock() { + if (isOnSkyblock) { + isOnSkyblock = false; + isInDungeons = false; + SkyblockEvents.LEAVE.invoker().onSkyblockLeave(); + } + } + + public static String getLocation() { + String location = null; + List sidebarLines = STRING_SCOREBOARD; + try { + if (sidebarLines != null) { + for (String sidebarLine : sidebarLines) { + if (sidebarLine.contains("⏣")) location = sidebarLine; + if (sidebarLine.contains("ф")) location = sidebarLine; //Rift + } + if (location == null) location = "Unknown"; + location = location.strip(); + } + } catch (IndexOutOfBoundsException e) { + LOGGER.error("[Skyblocker] Failed to get location from sidebar", e); + } + return location; + } + + public static double getPurse() { + String purseString = null; + double purse = 0; + + List sidebarLines = STRING_SCOREBOARD; + try { + + if (sidebarLines != null) { + for (String sidebarLine : sidebarLines) { + if (sidebarLine.contains("Piggy:")) purseString = sidebarLine; + if (sidebarLine.contains("Purse:")) purseString = sidebarLine; + } + } + if (purseString != null) purse = Double.parseDouble(purseString.replaceAll("[^0-9.]", "").strip()); + else purse = 0; + + } catch (IndexOutOfBoundsException e) { + LOGGER.error("[Skyblocker] Failed to get purse from sidebar", e); + } + return purse; + } + + public static int getBits() { + int bits = 0; + String bitsString = null; + List sidebarLines = STRING_SCOREBOARD; + try { + if (sidebarLines != null) { + for (String sidebarLine : sidebarLines) { + if (sidebarLine.contains("Bits")) bitsString = sidebarLine; + } + } + if (bitsString != null) { + bits = Integer.parseInt(bitsString.replaceAll("[^0-9.]", "").strip()); + } + } catch (IndexOutOfBoundsException e) { + LOGGER.error("[Skyblocker] Failed to get bits from sidebar", e); + } + return bits; + } + + private static void updateScoreboard(MinecraftClient client) { + try { + TEXT_SCOREBOARD.clear(); + STRING_SCOREBOARD.clear(); + + ClientPlayerEntity player = client.player; + if (player == null) return; + + Scoreboard scoreboard = player.getScoreboard(); + ScoreboardObjective objective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.FROM_ID.apply(1)); + ObjectArrayList textLines = new ObjectArrayList<>(); + ObjectArrayList stringLines = new ObjectArrayList<>(); + + for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) { + Team team = scoreboard.getPlayerTeam(score.getPlayerName()); + + if (team != null) { + Text textLine = Text.empty().append(team.getPrefix().copy()).append(team.getSuffix().copy()); + String strLine = team.getPrefix().getString() + team.getSuffix().getString(); + + if (!strLine.trim().isEmpty()) { + String formatted = Formatting.strip(strLine); + + textLines.add(textLine); + stringLines.add(formatted); + } + } + } + + if (objective != null) { + stringLines.add(objective.getDisplayName().getString()); + textLines.add(Text.empty().append(objective.getDisplayName().copy())); + + Collections.reverse(stringLines); + Collections.reverse(textLines); + } + + TEXT_SCOREBOARD.addAll(textLines); + STRING_SCOREBOARD.addAll(stringLines); + } catch (NullPointerException e) { + //Do nothing + } + } + + private static void updateFromPlayerList(MinecraftClient client) { + if (client.getNetworkHandler() == null) { + return; + } + for (PlayerListEntry playerListEntry : client.getNetworkHandler().getPlayerList()) { + if (playerListEntry.getDisplayName() == null) { + continue; + } + String name = playerListEntry.getDisplayName().getString(); + if (name.startsWith(PROFILE_PREFIX)) { + profile = name.substring(PROFILE_PREFIX.length()); + } + } + } + + public static void onClientWorldJoin(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client) { + clientWorldJoinTime = System.currentTimeMillis(); + resetLocRawInfo(); + } + + /** + * Sends /locraw to the server if the player is on skyblock and on a new island. + */ + private static void updateLocRaw() { + if (isOnSkyblock) { + long currentTime = System.currentTimeMillis(); + if (!sentLocRaw && canSendLocRaw && currentTime > clientWorldJoinTime + 1000) { + MessageScheduler.INSTANCE.sendMessageAfterCooldown("/locraw"); + sentLocRaw = true; + canSendLocRaw = false; + } + } else { + resetLocRawInfo(); + } + } + + /** + * Parses the /locraw reply from the server + * + * @return not display the message in chat is the command is sent by the mod + */ + public static boolean onChatMessage(Text text, boolean overlay) { + String message = text.getString(); + if (message.startsWith("{\"server\":") && message.endsWith("}")) { + JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject(); + if (locRaw.has("server")) { + server = locRaw.get("server").getAsString(); + if (locRaw.has("gameType")) { + gameType = locRaw.get("gameType").getAsString(); + } + if (locRaw.has("mode")) { + locationRaw = locRaw.get("mode").getAsString(); + } + if (locRaw.has("map")) { + map = locRaw.get("map").getAsString(); + } + + boolean shouldFilter = !sentLocRaw; + sentLocRaw = false; + + return shouldFilter; + } + } + return true; + } + + private static void resetLocRawInfo() { + sentLocRaw = false; + canSendLocRaw = true; + server = ""; + gameType = ""; + locationRaw = ""; + map = ""; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatFilterResult.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatFilterResult.java new file mode 100644 index 00000000..5a94682a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatFilterResult.java @@ -0,0 +1,18 @@ +package de.hysky.skyblocker.utils.chat; + +import net.minecraft.client.resource.language.I18n; +public enum ChatFilterResult { + // Skip this one / no action + PASS, + // Filter + FILTER, + // Move to action bar + ACTION_BAR; + // Skip remaining checks, don't filter + // null + + @Override + public String toString() { + return I18n.translate("text.autoconfig.skyblocker.option.messages.chatFilterResult." + name()); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java new file mode 100644 index 00000000..7892445e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java @@ -0,0 +1,89 @@ +package de.hysky.skyblocker.utils.chat; + +import de.hysky.skyblocker.skyblock.filters.*; +import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.skyblock.barn.HungryHiker; +import de.hysky.skyblocker.skyblock.barn.TreasureHunter; +import de.hysky.skyblocker.skyblock.dungeon.Reparty; +import de.hysky.skyblocker.skyblock.dungeon.ThreeWeirdos; +import de.hysky.skyblocker.skyblock.dungeon.Trivia; +import de.hysky.skyblocker.skyblock.dwarven.Fetchur; +import de.hysky.skyblocker.skyblock.dwarven.Puzzler; +import de.hysky.skyblocker.skyblock.filters.*; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.event.Event; +import net.fabricmc.fabric.api.event.EventFactory; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.text.Text; + +@FunctionalInterface +public interface ChatMessageListener { + /** + * An event called when a game message is received. Register your listeners in {@link ChatMessageListener#init()}. + */ + Event EVENT = EventFactory.createArrayBacked(ChatMessageListener.class, + (listeners) -> (message, asString) -> { + for (ChatMessageListener listener : listeners) { + ChatFilterResult result = listener.onMessage(message, asString); + if (result != ChatFilterResult.PASS) return result; + } + return ChatFilterResult.PASS; + }); + + /** + * Registers {@link ChatMessageListener}s to {@link ChatMessageListener#EVENT} and registers {@link ChatMessageListener#EVENT} to {@link ClientReceiveMessageEvents#ALLOW_GAME} + */ + static void init() { + ChatMessageListener[] listeners = new ChatMessageListener[]{ + // Features + new Fetchur(), + new Puzzler(), + new Reparty(), + new ThreeWeirdos(), + new Trivia(), + new TreasureHunter(), + new HungryHiker(), + // Filters + new AbilityFilter(), + new AdFilter(), + new AoteFilter(), + new ComboFilter(), + new HealFilter(), + new ImplosionFilter(), + new MoltenWaveFilter(), + new TeleportPadFilter(), + new AutopetFilter(), + new ShowOffFilter() + }; + // Register all listeners to EVENT + for (ChatMessageListener listener : listeners) { + EVENT.register(listener); + } + // Register EVENT to ClientReceiveMessageEvents.ALLOW_GAME from fabric api + ClientReceiveMessageEvents.ALLOW_GAME.register((message, overlay) -> { + if (!Utils.isOnSkyblock()) { + return true; + } + ChatFilterResult result = EVENT.invoker().onMessage(message, message.getString()); + switch (result) { + case ACTION_BAR -> { + if (overlay) { + return true; + } + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (player != null) { + player.sendMessage(message, true); + return false; + } + } + case FILTER -> { + return false; + } + } + return true; + }); + } + + ChatFilterResult onMessage(Text message, String asString); +} diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java new file mode 100644 index 00000000..708af280 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java @@ -0,0 +1,30 @@ +package de.hysky.skyblocker.utils.chat; + +import net.minecraft.text.Text; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class ChatPatternListener implements ChatMessageListener { + protected static final String NUMBER = "-?[0-9]{1,3}(?>,[0-9]{3})*(?:\\.[1-9])?"; + public final Pattern pattern; + + public ChatPatternListener(String pattern) { + this.pattern = Pattern.compile(pattern); + } + + @Override + public final ChatFilterResult onMessage(Text message, String asString) { + ChatFilterResult state = state(); + if (state == ChatFilterResult.PASS) return ChatFilterResult.PASS; + Matcher m = pattern.matcher(asString); + if (m.matches() && onMatch(message, m)) { + return state; + } + return ChatFilterResult.PASS; + } + + protected abstract ChatFilterResult state(); + + protected abstract boolean onMatch(Text message, Matcher matcher); +} diff --git a/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java b/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java new file mode 100644 index 00000000..f0589a0b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java @@ -0,0 +1,122 @@ +package de.hysky.skyblocker.utils.discord; + + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.Utils; +import meteordevelopment.discordipc.DiscordIPC; +import meteordevelopment.discordipc.RichPresence; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DecimalFormat; +import java.util.concurrent.CompletableFuture; + +/** + * Manages the discord rich presence. Automatically connects to discord and displays a customizable activity when playing Skyblock. + */ +public class DiscordRPCManager { + public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###,###.##"); + public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Discord RPC"); + /** + * The update task used to avoid multiple update tasks running simultaneously. + */ + public static CompletableFuture updateTask; + public static long startTimeStamp; + public static int cycleCount; + + public static void init() { + SkyblockEvents.LEAVE.register(DiscordRPCManager::initAndUpdatePresence); + SkyblockEvents.JOIN.register(() -> { + startTimeStamp = System.currentTimeMillis(); + initAndUpdatePresence(true); + }); + } + + /** + * Checks the {@link SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. + */ + public static void updateDataAndPresence() { + // If the custom message is empty, discord will keep the last message, this is can serve as a default if the user doesn't want a custom message + if (SkyblockerConfigManager.get().richPresence.customMessage.isEmpty()) { + SkyblockerConfigManager.get().richPresence.customMessage = "Playing Skyblock"; + SkyblockerConfigManager.save(); + } + if (SkyblockerConfigManager.get().richPresence.cycleMode) cycleCount = (cycleCount + 1) % 3; + initAndUpdatePresence(); + } + + /** + * @see #initAndUpdatePresence(boolean) + */ + private static void initAndUpdatePresence() { + initAndUpdatePresence(false); + } + + /** + * Updates discord presence asynchronously. + *

    + * When the {@link #updateTask previous update} does not exist or {@link CompletableFuture#isDone() has completed}: + *

    + * Connects to discord if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}, + * the player {@link Utils#isOnSkyblock() is on Skyblock}, and {@link DiscordIPC#isConnected() discord is not already connected}. + * Updates the presence if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled} + * and the player {@link Utils#isOnSkyblock() is on Skyblock}. + * Stops the connection if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled} + * or the player {@link Utils#isOnSkyblock() is not on Skyblock} and {@link DiscordIPC#isConnected() discord is connected}. + * Saves the update task in {@link #updateTask} + * + * @param initialization whether this is the first time the presence is being updates. If {@code true}, a message will be logged + * if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}. + */ + private static void initAndUpdatePresence(boolean initialization) { + if (updateTask == null || updateTask.isDone()) { + updateTask = CompletableFuture.runAsync(() -> { + if (SkyblockerConfigManager.get().richPresence.enableRichPresence && Utils.isOnSkyblock()) { + if (!DiscordIPC.isConnected()) { + if (DiscordIPC.start(934607927837356052L, null)) { + LOGGER.info("Discord RPC started successfully"); + } else { + LOGGER.error("Discord RPC failed to start"); + return; + } + } + DiscordIPC.setActivity(buildPresence()); + } else if (DiscordIPC.isConnected()) { + DiscordIPC.stop(); + LOGGER.info("Discord RPC stopped"); + } else if (initialization) { + LOGGER.info("Discord RPC is currently disabled"); + } + }); + } + } + + public static RichPresence buildPresence() { + RichPresence presence = new RichPresence(); + presence.setLargeImage("skyblocker-default", null); + presence.setStart(startTimeStamp); + presence.setDetails(SkyblockerConfigManager.get().richPresence.customMessage); + presence.setState(getInfo()); + return presence; + } + + public static String getInfo() { + String info = null; + if (!SkyblockerConfigManager.get().richPresence.cycleMode) { + switch (SkyblockerConfigManager.get().richPresence.info) { + case BITS -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits()); + case PURSE -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse()); + case LOCATION -> info = Utils.getLocation(); + } + } else if (SkyblockerConfigManager.get().richPresence.cycleMode) { + switch (cycleCount) { + case 0 -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits()); + case 1 -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse()); + case 2 -> info = Utils.getLocation(); + } + } + return info; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/FrustumUtils.java b/src/main/java/de/hysky/skyblocker/utils/render/FrustumUtils.java new file mode 100644 index 00000000..3fe79e43 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/FrustumUtils.java @@ -0,0 +1,21 @@ +package de.hysky.skyblocker.utils.render; + +import de.hysky.skyblocker.mixin.accessor.FrustumInvoker; +import de.hysky.skyblocker.mixin.accessor.WorldRendererAccessor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.Frustum; +import net.minecraft.util.math.Box; + +public class FrustumUtils { + public static Frustum getFrustum() { + return ((WorldRendererAccessor) MinecraftClient.getInstance().worldRenderer).getFrustum(); + } + + public static boolean isVisible(Box box) { + return getFrustum().isVisible(box); + } + + public static boolean isVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { + return ((FrustumInvoker) getFrustum()).invokeIsVisible(minX, minY, minZ, maxX, maxY, maxZ); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java new file mode 100644 index 00000000..4630149c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java @@ -0,0 +1,247 @@ +package de.hysky.skyblocker.utils.render; + +import com.mojang.blaze3d.platform.GlStateManager.DstFactor; +import com.mojang.blaze3d.platform.GlStateManager.SrcFactor; +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.mixin.accessor.BeaconBlockEntityRendererInvoker; +import me.x150.renderer.render.Renderer3d; +import de.hysky.skyblocker.utils.render.culling.OcclusionCulling; +import de.hysky.skyblocker.utils.render.title.Title; +import de.hysky.skyblocker.utils.render.title.TitleContainer; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.render.*; +import net.minecraft.client.render.VertexFormat.DrawMode; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.sound.SoundEvents; +import net.minecraft.text.OrderedText; +import net.minecraft.text.Text; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Vec3d; +import org.joml.Matrix3f; +import org.joml.Matrix4f; +import org.joml.Vector3f; +import org.lwjgl.opengl.GL11; + +import java.awt.*; + +public class RenderHelper { + private static final Vec3d ONE = new Vec3d(1, 1, 1); + private static final int MAX_OVERWORLD_BUILD_HEIGHT = 319; + private static final MinecraftClient client = MinecraftClient.getInstance(); + + public static void renderFilledThroughWallsWithBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { + renderFilledThroughWalls(context, pos, colorComponents, alpha); + renderBeaconBeam(context, pos, colorComponents); + } + + public static void renderFilledThroughWalls(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { + if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) { + Renderer3d.renderThroughWalls(); + renderFilled(context, pos, colorComponents, alpha); + Renderer3d.stopRenderThroughWalls(); + } + } + + public static void renderFilledIfVisible(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { + if (OcclusionCulling.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) { + renderFilled(context, pos, colorComponents, alpha); + } + } + + private static void renderFilled(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { + Renderer3d.renderFilled(context.matrixStack(), new Color(colorComponents[0], colorComponents[1], colorComponents[2], alpha), Vec3d.of(pos), ONE); + } + + private static void renderBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents) { + if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, MAX_OVERWORLD_BUILD_HEIGHT, pos.getZ() + 1)) { + MatrixStack matrices = context.matrixStack(); + Vec3d camera = context.camera().getPos(); + + matrices.push(); + matrices.translate(pos.getX() - camera.getX(), pos.getY() - camera.getY(), pos.getZ() - camera.getZ()); + + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + VertexConsumerProvider.Immediate consumer = VertexConsumerProvider.immediate(buffer); + + BeaconBlockEntityRendererInvoker.renderBeam(matrices, consumer, context.tickDelta(), context.world().getTime(), 0, MAX_OVERWORLD_BUILD_HEIGHT, colorComponents); + + consumer.draw(); + matrices.pop(); + } + } + + /** + * Renders the outline of a box with the specified color components and line width. + * This does not use renderer since renderer draws outline using debug lines with a fixed width. + */ + public static void renderOutline(WorldRenderContext context, Box box, float[] colorComponents, float lineWidth) { + if (FrustumUtils.isVisible(box)) { + MatrixStack matrices = context.matrixStack(); + Vec3d camera = context.camera().getPos(); + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + + RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram); + RenderSystem.setShaderColor(1f, 1f, 1f, 1f); + RenderSystem.lineWidth(lineWidth); + RenderSystem.disableCull(); + RenderSystem.enableDepthTest(); + + matrices.push(); + matrices.translate(-camera.getX(), -camera.getY(), -camera.getZ()); + + buffer.begin(DrawMode.LINES, VertexFormats.LINES); + WorldRenderer.drawBox(matrices, buffer, box, colorComponents[0], colorComponents[1], colorComponents[2], 1f); + tessellator.draw(); + + matrices.pop(); + RenderSystem.lineWidth(1f); + RenderSystem.enableCull(); + RenderSystem.disableDepthTest(); + } + } + + /** + * Draws lines from point to point.

    + *

    + * Tip: To draw lines from the center of a block, offset the X, Y and Z each by 0.5 + * + * @param context The WorldRenderContext which supplies the matrices and tick delta + * @param points The points from which to draw lines between + * @param colorComponents An array of R, G and B color components + * @param alpha The alpha of the lines + * @param lineWidth The width of the lines + */ + public static void renderLinesFromPoints(WorldRenderContext context, Vec3d[] points, float[] colorComponents, float alpha, float lineWidth) { + Vec3d camera = context.camera().getPos(); + MatrixStack matrices = context.matrixStack(); + + matrices.push(); + matrices.translate(-camera.x, -camera.y, -camera.z); + + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + Matrix4f projectionMatrix = matrices.peek().getPositionMatrix(); + Matrix3f normalMatrix = matrices.peek().getNormalMatrix(); + + GL11.glEnable(GL11.GL_LINE_SMOOTH); + GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST); + + RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram); + RenderSystem.setShaderColor(1f, 1f, 1f, 1f); + RenderSystem.lineWidth(lineWidth); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA); + RenderSystem.disableCull(); + RenderSystem.enableDepthTest(); + + buffer.begin(DrawMode.LINE_STRIP, VertexFormats.LINES); + + for (int i = 0; i < points.length; i++) { + Vec3d point = points[i]; + Vec3d nextPoint = (i + 1 == points.length) ? points[i - 1] : points[i + 1]; + Vector3f normalVec = new Vector3f((float) nextPoint.getX(), (float) nextPoint.getY(), (float) nextPoint.getZ()).sub((float) point.getX(), (float) point.getY(), (float) point.getZ()).normalize(); + + buffer + .vertex(projectionMatrix, (float) point.getX(), (float) point.getY(), (float) point.getZ()) + .color(colorComponents[0], colorComponents[1], colorComponents[2], alpha) + .normal(normalMatrix, normalVec.x, normalVec.y, normalVec.z) + .next(); + } + + tessellator.draw(); + + matrices.pop(); + GL11.glDisable(GL11.GL_LINE_SMOOTH); + RenderSystem.lineWidth(1f); + RenderSystem.disableBlend(); + RenderSystem.defaultBlendFunc(); + RenderSystem.enableCull(); + RenderSystem.disableDepthTest(); + } + + public static void renderText(WorldRenderContext context, Text text, Vec3d pos, boolean seeThrough) { + renderText(context, text, pos, 1, seeThrough); + } + + public static void renderText(WorldRenderContext context, Text text, Vec3d pos, float scale, boolean seeThrough) { + renderText(context, text, pos, scale, 0, seeThrough); + } + + public static void renderText(WorldRenderContext context, Text text, Vec3d pos, float scale, float yOffset, boolean seeThrough) { + renderText(context, text.asOrderedText(), pos, scale, yOffset, seeThrough); + } + + /** + * Renders text in the world space. + * + * @param seeThrough Whether the text should be able to be seen through walls or not. + */ + public static void renderText(WorldRenderContext context, OrderedText text, Vec3d pos, float scale, float yOffset, boolean seeThrough) { + MatrixStack matrices = context.matrixStack(); + Vec3d camera = context.camera().getPos(); + TextRenderer textRenderer = client.textRenderer; + + scale *= 0.025f; + + matrices.push(); + matrices.translate(pos.getX() - camera.getX(), pos.getY() - camera.getY(), pos.getZ() - camera.getZ()); + matrices.peek().getPositionMatrix().mul(RenderSystem.getModelViewMatrix()); + matrices.multiply(context.camera().getRotation()); + matrices.scale(-scale, -scale, scale); + + Matrix4f positionMatrix = matrices.peek().getPositionMatrix(); + float xOffset = -textRenderer.getWidth(text) / 2f; + + Tessellator tessellator = RenderSystem.renderThreadTesselator(); + BufferBuilder buffer = tessellator.getBuffer(); + VertexConsumerProvider.Immediate consumers = VertexConsumerProvider.immediate(buffer); + + RenderSystem.depthFunc(seeThrough ? GL11.GL_ALWAYS : GL11.GL_LEQUAL); + + textRenderer.draw(text, xOffset, yOffset, 0xFFFFFFFF, false, positionMatrix, consumers, TextRenderer.TextLayerType.SEE_THROUGH, 0, LightmapTextureManager.MAX_LIGHT_COORDINATE); + consumers.draw(); + + RenderSystem.depthFunc(GL11.GL_LEQUAL); + matrices.pop(); + } + + /** + * Adds the title to {@link TitleContainer} and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already. + * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller. + * + * @param title the title + */ + public static void displayInTitleContainerAndPlaySound(Title title) { + if (TitleContainer.addTitle(title)) { + playNotificationSound(); + } + } + + /** + * Adds the title to {@link TitleContainer} for a set number of ticks and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already. + * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller. + * + * @param title the title + * @param ticks the number of ticks the title will remain + */ + public static void displayInTitleContainerAndPlaySound(Title title, int ticks) { + if (TitleContainer.addTitle(title, ticks)) { + playNotificationSound(); + } + } + + private static void playNotificationSound() { + if (MinecraftClient.getInstance().player != null) { + MinecraftClient.getInstance().player.playSound(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 100f, 0.1f); + } + } + + public static boolean pointIsInArea(double x, double y, double x1, double y1, double x2, double y2) { + return x >= x1 && x <= x2 && y >= y1 && y <= y2; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/culling/OcclusionCulling.java b/src/main/java/de/hysky/skyblocker/utils/render/culling/OcclusionCulling.java new file mode 100644 index 00000000..5f8d1592 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/culling/OcclusionCulling.java @@ -0,0 +1,47 @@ +package de.hysky.skyblocker.utils.render.culling; + +import com.logisticscraft.occlusionculling.OcclusionCullingInstance; +import com.logisticscraft.occlusionculling.cache.ArrayOcclusionCache; +import com.logisticscraft.occlusionculling.util.Vec3d; +import de.hysky.skyblocker.utils.render.FrustumUtils; +import net.minecraft.client.MinecraftClient; + +public class OcclusionCulling { + private static final int TRACING_DISTANCE = 128; + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static OcclusionCullingInstance instance = null; + + // Reused objects to reduce allocation overhead + private static final Vec3d cameraPos = new Vec3d(0, 0, 0); + private static final Vec3d min = new Vec3d(0, 0, 0); + private static final Vec3d max = new Vec3d(0, 0, 0); + + /** + * Initializes the occlusion culling instance + */ + public static void init() { + instance = new OcclusionCullingInstance(TRACING_DISTANCE, new WorldProvider(), new ArrayOcclusionCache(TRACING_DISTANCE), 2); + } + + private static void updateCameraPos() { + var camera = CLIENT.gameRenderer.getCamera().getPos(); + cameraPos.set(camera.x, camera.y, camera.z); + } + + /** + * This first checks checks if the bounding box is within the camera's FOV, if + * it is then it checks for whether it's occluded or not. + * + * @return A boolean representing whether the bounding box is fully visible or + * not. + */ + public static boolean isVisible(double x1, double y1, double z1, double x2, double y2, double z2) { + if (!FrustumUtils.isVisible(x1, y1, z1, x2, y2, z2)) return false; + + updateCameraPos(); + min.set(x1, y1, z1); + max.set(x2, y2, z2); + + return instance.isAABBVisible(min, max, cameraPos); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/culling/WorldProvider.java b/src/main/java/de/hysky/skyblocker/utils/render/culling/WorldProvider.java new file mode 100644 index 00000000..7ee0f0ed --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/culling/WorldProvider.java @@ -0,0 +1,28 @@ +package de.hysky.skyblocker.utils.render.culling; + +import com.logisticscraft.occlusionculling.DataProvider; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.BlockPos; + +public class WorldProvider implements DataProvider { + private final static MinecraftClient CLIENT = MinecraftClient.getInstance(); + private ClientWorld world = null; + + @Override + public boolean prepareChunk(int chunkX, int chunkZ) { + this.world = CLIENT.world; + return this.world != null; + } + + @Override + public boolean isOpaqueFullCube(int x, int y, int z) { + BlockPos pos = new BlockPos(x, y, z); + return this.world.getBlockState(pos).isOpaqueFullCube(this.world, pos); + } + + @Override + public void cleanup() { + this.world = null; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/culling/package-info.java b/src/main/java/de/hysky/skyblocker/utils/render/culling/package-info.java new file mode 100644 index 00000000..1d5cdf98 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/culling/package-info.java @@ -0,0 +1,4 @@ +/** + * Package dedicated to occlusion culling utilities + */ +package de.hysky.skyblocker.utils.render.culling; \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ColorHighlight.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ColorHighlight.java new file mode 100644 index 00000000..5451e1a8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ColorHighlight.java @@ -0,0 +1,24 @@ +package de.hysky.skyblocker.utils.render.gui; + +public record ColorHighlight(int slot, int color) { + private static final int RED_HIGHLIGHT = 64 << 24 | 255 << 16; + private static final int YELLOW_HIGHLIGHT = 128 << 24 | 255 << 16 | 255 << 8; + private static final int GREEN_HIGHLIGHT = 128 << 24 | 64 << 16 | 196 << 8 | 64; + private static final int GRAY_HIGHLIGHT = 128 << 24 | 64 << 16 | 64 << 8 | 64; + + public static ColorHighlight red(int slot) { + return new ColorHighlight(slot, RED_HIGHLIGHT); + } + + public static ColorHighlight yellow(int slot) { + return new ColorHighlight(slot, YELLOW_HIGHLIGHT); + } + + public static ColorHighlight green(int slot) { + return new ColorHighlight(slot, GREEN_HIGHLIGHT); + } + + public static ColorHighlight gray(int slot) { + return new ColorHighlight(slot, GRAY_HIGHLIGHT); + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java new file mode 100644 index 00000000..cf67e84c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java @@ -0,0 +1,44 @@ +package de.hysky.skyblocker.utils.render.gui; + +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.item.ItemStack; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Abstract class for gui solvers. Extend this class to add a new gui solver, like terminal solvers or experiment solvers. + */ +public abstract class ContainerSolver { + private final Pattern containerName; + + protected ContainerSolver(String containerName) { + this.containerName = Pattern.compile(containerName); + } + + protected abstract boolean isEnabled(); + + public Pattern getName() { + return containerName; + } + + protected void start(GenericContainerScreen screen) { + } + + protected void reset() { + } + + protected abstract List getColors(String[] groups, Map slots); + + protected void trimEdges(Map slots, int rows) { + for (int i = 0; i < rows; i++) { + slots.remove(9 * i); + slots.remove(9 * i + 8); + } + for (int i = 1; i < 8; i++) { + slots.remove(i); + slots.remove((rows - 1) * 9 + i); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java new file mode 100644 index 00000000..60e2b4e4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java @@ -0,0 +1,125 @@ +package de.hysky.skyblocker.utils.render.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import de.hysky.skyblocker.mixin.accessor.HandledScreenAccessor; +import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; +import de.hysky.skyblocker.skyblock.dungeon.terminal.ColorTerminal; +import de.hysky.skyblocker.skyblock.dungeon.terminal.OrderTerminal; +import de.hysky.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal; +import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; +import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; +import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver; +import de.hysky.skyblocker.utils.Utils; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Manager class for {@link ContainerSolver}s like terminal solvers and experiment solvers. To add a new gui solver, extend {@link ContainerSolver} and register it in {@link #ContainerSolverManager()}. + */ +public class ContainerSolverManager { + private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(""); + private final ContainerSolver[] solvers; + private ContainerSolver currentSolver = null; + private String[] groups; + private List highlights; + + public ContainerSolverManager() { + solvers = new ContainerSolver[]{ + new ColorTerminal(), + new OrderTerminal(), + new StartsWithTerminal(), + new CroesusHelper(), + new ChronomatronSolver(), + new SuperpairsSolver(), + new UltrasequencerSolver() + }; + } + + public ContainerSolver getCurrentSolver() { + return currentSolver; + } + + public void init() { + ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) { + ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, delta) -> { + MatrixStack matrices = context.getMatrices(); + matrices.push(); + matrices.translate(((HandledScreenAccessor) genericContainerScreen).getX(), ((HandledScreenAccessor) genericContainerScreen).getY(), 300); + onDraw(context, genericContainerScreen.getScreenHandler().slots.subList(0, genericContainerScreen.getScreenHandler().getRows() * 9)); + matrices.pop(); + }); + ScreenEvents.remove(screen).register(screen1 -> clearScreen()); + onSetScreen(genericContainerScreen); + } else { + clearScreen(); + } + }); + } + + public void onSetScreen(@NotNull GenericContainerScreen screen) { + String screenName = screen.getTitle().getString(); + Matcher matcher = PLACEHOLDER_PATTERN.matcher(screenName); + for (ContainerSolver solver : solvers) { + if (solver.isEnabled()) { + matcher.usePattern(solver.getName()); + matcher.reset(); + if (matcher.matches()) { + currentSolver = solver; + groups = new String[matcher.groupCount()]; + for (int i = 0; i < groups.length; i++) { + groups[i] = matcher.group(i + 1); + } + currentSolver.start(screen); + return; + } + } + } + clearScreen(); + } + + public void clearScreen() { + if (currentSolver != null) { + currentSolver.reset(); + currentSolver = null; + } + } + + public void markDirty() { + highlights = null; + } + + public void onDraw(DrawContext context, List slots) { + if (currentSolver == null) + return; + if (highlights == null) + highlights = currentSolver.getColors(groups, slotMap(slots)); + RenderSystem.enableDepthTest(); + RenderSystem.colorMask(true, true, true, false); + for (ColorHighlight highlight : highlights) { + Slot slot = slots.get(highlight.slot()); + int color = highlight.color(); + context.fillGradient(slot.x, slot.y, slot.x + 16, slot.y + 16, color, color); + } + RenderSystem.colorMask(true, true, true, true); + } + + private Map slotMap(List slots) { + Map slotMap = new TreeMap<>(); + for (int i = 0; i < slots.size(); i++) { + slotMap.put(i, slots.get(i).getStack()); + } + return slotMap; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/title/Title.java b/src/main/java/de/hysky/skyblocker/utils/render/title/Title.java new file mode 100644 index 00000000..1e167afa --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/title/Title.java @@ -0,0 +1,53 @@ +package de.hysky.skyblocker.utils.render.title; + +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +/** + * Represents a title used for {@link TitleContainer}. + * + * @see TitleContainer + */ +public class Title { + private MutableText text; + protected float x = -1; + protected float y = -1; + + /** + * Constructs a new title with the given translation key and formatting to be applied. + * + * @param textKey the translation key + * @param formatting the formatting to be applied to the text + */ + public Title(String textKey, Formatting formatting) { + this(Text.translatable(textKey).formatted(formatting)); + } + + /** + * Constructs a new title with the given {@link MutableText}. + * Use {@link Text#literal(String)} or {@link Text#translatable(String)} to create a {@link MutableText} + * + * @param text the mutable text + */ + public Title(MutableText text) { + this.text = text; + } + + public MutableText getText() { + return text; + } + + public void setText(MutableText text) { + this.text = text; + } + + protected boolean isDefaultPos() { + return x == -1 && y == -1; + } + + protected void resetPos() { + this.x = -1; + this.y = -1; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainer.java b/src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainer.java new file mode 100644 index 00000000..487e3d8b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainer.java @@ -0,0 +1,175 @@ +package de.hysky.skyblocker.utils.render.title; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.util.math.MathHelper; + +import java.util.LinkedHashSet; +import java.util.Set; + +public class TitleContainer { + /** + * The set of titles which will be rendered. + * + * @see #containsTitle(Title) + * @see #addTitle(Title) + * @see #addTitle(Title, int) + * @see #removeTitle(Title) + */ + private static final Set titles = new LinkedHashSet<>(); + + public static void init() { + HudRenderCallback.EVENT.register(TitleContainer::render); + ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") + .then(ClientCommandManager.literal("hud") + .then(ClientCommandManager.literal("titleContainer") + .executes(Scheduler.queueOpenScreenCommand(TitleContainerConfigScreen::new)))))); + } + + /** + * Returns {@code true} if the title is currently shown. + * + * @param title the title to check + * @return whether the title in currently shown + */ + public static boolean containsTitle(Title title) { + return titles.contains(title); + } + + /** + * Adds a title to be shown + * + * @param title the title to be shown + * @return whether the title is already currently being shown + */ + public static boolean addTitle(Title title) { + if (titles.add(title)) { + title.resetPos(); + return true; + } + return false; + } + + /** + * Adds a title to be shown for a set number of ticks + * + * @param title the title to be shown + * @param ticks the number of ticks to show the title + * @return whether the title is already currently being shown + */ + public static boolean addTitle(Title title, int ticks) { + if (addTitle(title)) { + Scheduler.INSTANCE.schedule(() -> TitleContainer.removeTitle(title), ticks); + return true; + } + return false; + } + + /** + * Stops showing a title + * + * @param title the title to stop showing + */ + public static void removeTitle(Title title) { + titles.remove(title); + } + + private static void render(DrawContext context, float tickDelta) { + render(context, titles, SkyblockerConfigManager.get().general.titleContainer.x, SkyblockerConfigManager.get().general.titleContainer.y, tickDelta); + } + + protected static void render(DrawContext context, Set<Title> titles, int xPos, int yPos, float tickDelta) { + var client = MinecraftClient.getInstance(); + TextRenderer textRenderer = client.textRenderer; + + // Calculate Scale to use + float scale = 3F * (SkyblockerConfigManager.get().general.titleContainer.titleContainerScale / 100F); + + // Grab direction and alignment values + SkyblockerConfig.Direction direction = SkyblockerConfigManager.get().general.titleContainer.direction; + SkyblockerConfig.Alignment alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; + // x/y refer to the starting position for the text + // y always starts at yPos + float x = 0; + float y = yPos; + + //Calculate the width of combined text + float width = 0; + for (Title title : titles) { + width += textRenderer.getWidth(title.getText()) * scale + 10; + } + + if (alignment == SkyblockerConfig.Alignment.MIDDLE) { + if (direction == SkyblockerConfig.Direction.HORIZONTAL) { + //If middle aligned horizontally, start the xPosition at half of the width to the left. + x = xPos - (width / 2); + } else { + //If middle aligned vertically, start at xPos, we will shift each text to the left later + x = xPos; + } + } + if (alignment == SkyblockerConfig.Alignment.LEFT || alignment == SkyblockerConfig.Alignment.RIGHT) { + //If left or right aligned, start at xPos, we will shift each text later + x = xPos; + } + + for (Title title : titles) { + + //Calculate which x the text should use + float xToUse; + if (direction == SkyblockerConfig.Direction.HORIZONTAL) { + xToUse = alignment == SkyblockerConfig.Alignment.RIGHT ? + x - (textRenderer.getWidth(title.getText()) * scale) : //if right aligned we need the text position to be aligned on the right side. + x; + } else { + xToUse = alignment == SkyblockerConfig.Alignment.MIDDLE ? + x - (textRenderer.getWidth(title.getText()) * scale) / 2 : //if middle aligned we need the text position to be aligned in the middle. + alignment == SkyblockerConfig.Alignment.RIGHT ? + x - (textRenderer.getWidth(title.getText()) * scale) : //if right aligned we need the text position to be aligned on the right side. + x; + } + + //Start displaying the title at the correct position, not at the default position + if (title.isDefaultPos()) { + title.x = xToUse; + title.y = y; + } + + //Lerp the texts x and y variables + title.x = MathHelper.lerp(tickDelta * 0.5F, title.x, xToUse); + title.y = MathHelper.lerp(tickDelta * 0.5F, title.y, y); + + //Translate the matrix to the texts position and scale + context.getMatrices().push(); + context.getMatrices().translate(title.x, title.y, 200); + context.getMatrices().scale(scale, scale, scale); + + //Draw text + context.drawTextWithShadow(textRenderer, title.getText(), 0, 0, 0xFFFFFF); + context.getMatrices().pop(); + + //Calculate the x and y positions for the next title + if (direction == SkyblockerConfig.Direction.HORIZONTAL) { + if (alignment == SkyblockerConfig.Alignment.MIDDLE || alignment == SkyblockerConfig.Alignment.LEFT) { + //Move to the right if middle or left aligned + x += textRenderer.getWidth(title.getText()) * scale + 10; + } + + if (alignment == SkyblockerConfig.Alignment.RIGHT) { + //Move to the left if right aligned + x -= textRenderer.getWidth(title.getText()) * scale + 10; + } + } else { + //Y always moves by the same amount if vertical + y += textRenderer.fontHeight * scale + 10; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainerConfigScreen.java b/src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainerConfigScreen.java new file mode 100644 index 00000000..5a42eeb4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainerConfigScreen.java @@ -0,0 +1,170 @@ +package de.hysky.skyblocker.utils.render.title; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.render.RenderHelper; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.util.math.Vector2f; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import net.minecraft.util.Pair; +import org.lwjgl.glfw.GLFW; + +import java.awt.*; +import java.util.Set; + +public class TitleContainerConfigScreen extends Screen { + private final Title example1 = new Title(Text.literal("Test1").formatted(Formatting.RED)); + private final Title example2 = new Title(Text.literal("Test23").formatted(Formatting.AQUA)); + private final Title example3 = new Title(Text.literal("Testing1234").formatted(Formatting.DARK_GREEN)); + private float hudX = SkyblockerConfigManager.get().general.titleContainer.x; + private float hudY = SkyblockerConfigManager.get().general.titleContainer.y; + private final Screen parent; + + protected TitleContainerConfigScreen() { + this(null); + } + + public TitleContainerConfigScreen(Screen parent) { + super(Text.of("Title Container HUD Config")); + this.parent = parent; + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + super.render(context, mouseX, mouseY, delta); + renderBackground(context, mouseX, mouseY, delta); + TitleContainer.render(context, Set.of(example1, example2, example3), (int) hudX, (int) hudY, delta); + SkyblockerConfig.Direction direction = SkyblockerConfigManager.get().general.titleContainer.direction; + SkyblockerConfig.Alignment alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; + context.drawCenteredTextWithShadow(textRenderer, "Press Q/E to change Alignment: " + alignment, width / 2, textRenderer.fontHeight * 2, Color.WHITE.getRGB()); + context.drawCenteredTextWithShadow(textRenderer, "Press R to change Direction: " + direction, width / 2, textRenderer.fontHeight * 3 + 5, Color.WHITE.getRGB()); + context.drawCenteredTextWithShadow(textRenderer, "Press +/- to change Scale", width / 2, textRenderer.fontHeight * 4 + 10, Color.WHITE.getRGB()); + context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, textRenderer.fontHeight * 5 + 15, Color.GRAY.getRGB()); + + Pair<Vector2f, Vector2f> boundingBox = getSelectionBoundingBox(); + int x1 = (int) boundingBox.getLeft().getX(); + int y1 = (int) boundingBox.getLeft().getY(); + int x2 = (int) boundingBox.getRight().getX(); + int y2 = (int) boundingBox.getRight().getY(); + + context.drawHorizontalLine(x1, x2, y1, Color.RED.getRGB()); + context.drawHorizontalLine(x1, x2, y2, Color.RED.getRGB()); + context.drawVerticalLine(x1, y1, y2, Color.RED.getRGB()); + context.drawVerticalLine(x2, y1, y2, Color.RED.getRGB()); + } + + private Pair<Vector2f, Vector2f> getSelectionBoundingBox() { + SkyblockerConfig.Alignment alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; + + float midWidth = getSelectionWidth() / 2F; + float x1 = 0; + float x2 = 0; + float y1 = hudY; + float y2 = hudY + getSelectionHeight(); + switch (alignment) { + case RIGHT -> { + x1 = hudX - midWidth * 2; + x2 = hudX; + } + case MIDDLE -> { + x1 = hudX - midWidth; + x2 = hudX + midWidth; + } + case LEFT -> { + x1 = hudX; + x2 = hudX + midWidth * 2; + } + } + return new Pair<>(new Vector2f(x1, y1), new Vector2f(x2, y2)); + } + + private float getSelectionHeight() { + float scale = (3F * (SkyblockerConfigManager.get().general.titleContainer.titleContainerScale / 100F)); + return SkyblockerConfigManager.get().general.titleContainer.direction == SkyblockerConfig.Direction.HORIZONTAL ? + (textRenderer.fontHeight * scale) : + (textRenderer.fontHeight + 10F) * 3F * scale; + } + + private float getSelectionWidth() { + float scale = (3F * (SkyblockerConfigManager.get().general.titleContainer.titleContainerScale / 100F)); + return SkyblockerConfigManager.get().general.titleContainer.direction == SkyblockerConfig.Direction.HORIZONTAL ? + (textRenderer.getWidth("Test1") + 10 + textRenderer.getWidth("Test23") + 10 + textRenderer.getWidth("Testing1234")) * scale : + textRenderer.getWidth("Testing1234") * scale; + } + + @Override + public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { + float midWidth = getSelectionWidth() / 2; + float midHeight = getSelectionHeight() / 2; + var alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; + + Pair<Vector2f, Vector2f> boundingBox = getSelectionBoundingBox(); + float x1 = boundingBox.getLeft().getX(); + float y1 = boundingBox.getLeft().getY(); + float x2 = boundingBox.getRight().getX(); + float y2 = boundingBox.getRight().getY(); + + if (RenderHelper.pointIsInArea(mouseX, mouseY, x1, y1, x2, y2) && button == 0) { + hudX = switch (alignment) { + case LEFT -> (int) mouseX - midWidth; + case MIDDLE -> (int) mouseX; + case RIGHT -> (int) mouseX + midWidth; + }; + hudY = (int) (mouseY - midHeight); + } + return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); + } + + @Override + public boolean mouseClicked(double mouseX, double mouseY, int button) { + if (button == 1) { + hudX = (float) this.width / 2; + hudY = this.height * 0.6F; + } + return super.mouseClicked(mouseX, mouseY, button); + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (keyCode == GLFW.GLFW_KEY_Q) { + SkyblockerConfig.Alignment current = SkyblockerConfigManager.get().general.titleContainer.alignment; + SkyblockerConfigManager.get().general.titleContainer.alignment = switch (current) { + case LEFT -> SkyblockerConfig.Alignment.MIDDLE; + case MIDDLE -> SkyblockerConfig.Alignment.RIGHT; + case RIGHT -> SkyblockerConfig.Alignment.LEFT; + }; + } + if (keyCode == GLFW.GLFW_KEY_E) { + SkyblockerConfig.Alignment current = SkyblockerConfigManager.get().general.titleContainer.alignment; + SkyblockerConfigManager.get().general.titleContainer.alignment = switch (current) { + case LEFT -> SkyblockerConfig.Alignment.RIGHT; + case MIDDLE -> SkyblockerConfig.Alignment.LEFT; + case RIGHT -> SkyblockerConfig.Alignment.MIDDLE; + }; + } + if (keyCode == GLFW.GLFW_KEY_R) { + SkyblockerConfig.Direction current = SkyblockerConfigManager.get().general.titleContainer.direction; + SkyblockerConfigManager.get().general.titleContainer.direction = switch (current) { + case HORIZONTAL -> SkyblockerConfig.Direction.VERTICAL; + case VERTICAL -> SkyblockerConfig.Direction.HORIZONTAL; + }; + } + if (keyCode == GLFW.GLFW_KEY_EQUAL) { + SkyblockerConfigManager.get().general.titleContainer.titleContainerScale += 10; + } + if (keyCode == GLFW.GLFW_KEY_MINUS) { + SkyblockerConfigManager.get().general.titleContainer.titleContainerScale -= 10; + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public void close() { + SkyblockerConfigManager.get().general.titleContainer.x = (int) hudX; + SkyblockerConfigManager.get().general.titleContainer.y = (int) hudY; + SkyblockerConfigManager.save(); + this.client.setScreen(parent); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/scheduler/MessageScheduler.java b/src/main/java/de/hysky/skyblocker/utils/scheduler/MessageScheduler.java new file mode 100644 index 00000000..15636965 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/scheduler/MessageScheduler.java @@ -0,0 +1,66 @@ +package de.hysky.skyblocker.utils.scheduler; + +import net.minecraft.client.MinecraftClient; + +/** + * A scheduler for sending chat messages or commands. Use the instance in {@link #INSTANCE}. Do not instantiate this class. + */ +public class MessageScheduler extends Scheduler { + /** + * The minimum delay that the server will accept between chat messages. + */ + private static final int MIN_DELAY = 200; + public static final MessageScheduler INSTANCE = new MessageScheduler(); + /** + * The timestamp of the last message send, + */ + private long lastMessage = 0; + + protected MessageScheduler() { + } + + /** + * Sends a chat message or command after the minimum cooldown. Prefer this method to send messages or commands to the server. + * + * @param message the message to send + */ + public void sendMessageAfterCooldown(String message) { + if (lastMessage + MIN_DELAY < System.currentTimeMillis()) { + sendMessage(message); + lastMessage = System.currentTimeMillis(); + } else { + queueMessage(message, 0); + } + } + + private void sendMessage(String message) { + if (MinecraftClient.getInstance().player != null) { + if (message.startsWith("/")) { + MinecraftClient.getInstance().player.networkHandler.sendCommand(message.substring(1)); + } else { + MinecraftClient.getInstance().inGameHud.getChatHud().addToMessageHistory(message); + MinecraftClient.getInstance().player.networkHandler.sendChatMessage(message); + } + } + } + + /** + * Queues a chat message or command to send in {@code delay} ticks. Use this method to send messages or commands a set time in the future. The minimum cooldown is still respected. + * + * @param message the message to send + * @param delay the delay before sending the message in ticks + */ + public void queueMessage(String message, int delay) { + schedule(() -> sendMessage(message), delay); + } + + @Override + protected boolean runTask(Runnable task) { + if (lastMessage + MIN_DELAY < System.currentTimeMillis()) { + task.run(); + lastMessage = System.currentTimeMillis(); + return true; + } + return false; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/scheduler/Scheduler.java b/src/main/java/de/hysky/skyblocker/utils/scheduler/Scheduler.java new file mode 100644 index 00000000..0f44cf93 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/scheduler/Scheduler.java @@ -0,0 +1,140 @@ +package de.hysky.skyblocker.utils.scheduler; + +import com.mojang.brigadier.Command; +import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +/** + * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link #INSTANCE}. Do not instantiate this class. + */ +public class Scheduler { + private static final Logger LOGGER = LoggerFactory.getLogger(Scheduler.class); + public static final Scheduler INSTANCE = new Scheduler(); + private int currentTick = 0; + private final AbstractInt2ObjectMap<List<ScheduledTask>> tasks = new Int2ObjectOpenHashMap<>(); + + protected Scheduler() { + } + + /** + * Schedules a task to run after a delay. + * + * @param task the task to run + * @param delay the delay in ticks + */ + public void schedule(Runnable task, int delay) { + if (delay >= 0) { + addTask(new ScheduledTask(task), currentTick + delay); + } else { + LOGGER.warn("Scheduled a task with negative delay"); + } + } + + /** + * Schedules a task to run every period ticks. + * + * @param task the task to run + * @param period the period in ticks + */ + public void scheduleCyclic(Runnable task, int period) { + if (period > 0) { + addTask(new CyclicTask(task, period), currentTick); + } else { + LOGGER.error("Attempted to schedule a cyclic task with period lower than 1"); + } + } + + public static Command<FabricClientCommandSource> queueOpenScreenCommand(Supplier<Screen> screenSupplier) { + return context -> INSTANCE.queueOpenScreen(screenSupplier); + } + + /** + * Schedules a screen to open in the next tick. Used in commands to avoid screen immediately closing after the command is executed. + * + * @param screenSupplier the supplier of the screen to open + * @see #queueOpenScreenCommand(Supplier) + */ + public int queueOpenScreen(Supplier<Screen> screenSupplier) { + MinecraftClient.getInstance().send(() -> MinecraftClient.getInstance().setScreen(screenSupplier.get())); + return Command.SINGLE_SUCCESS; + } + + public void tick() { + if (tasks.containsKey(currentTick)) { + List<ScheduledTask> currentTickTasks = tasks.get(currentTick); + //noinspection ForLoopReplaceableByForEach (or else we get a ConcurrentModificationException) + for (int i = 0; i < currentTickTasks.size(); i++) { + ScheduledTask task = currentTickTasks.get(i); + if (!runTask(task)) { + tasks.computeIfAbsent(currentTick + 1, key -> new ArrayList<>()).add(task); + } + } + tasks.remove(currentTick); + } + currentTick += 1; + } + + /** + * Runs the task if able. + * + * @param task the task to run + * @return {@code true} if the task is run, and {@link false} if task is not run. + */ + protected boolean runTask(Runnable task) { + task.run(); + return true; + } + + private void addTask(ScheduledTask scheduledTask, int schedule) { + if (tasks.containsKey(schedule)) { + tasks.get(schedule).add(scheduledTask); + } else { + List<ScheduledTask> list = new ArrayList<>(); + list.add(scheduledTask); + tasks.put(schedule, list); + } + } + + /** + * A task that runs every period ticks. More specifically, this task reschedules itself to run again after period ticks every time it runs. + */ + protected class CyclicTask extends ScheduledTask { + private final int period; + + CyclicTask(Runnable inner, int period) { + super(inner); + this.period = period; + } + + @Override + public void run() { + super.run(); + addTask(this, currentTick + period); + } + } + + /** + * A task that runs at a specific tick, relative to {@link #currentTick}. + */ + protected static class ScheduledTask implements Runnable { + private final Runnable inner; + + public ScheduledTask(Runnable inner) { + this.inner = inner; + } + + @Override + public void run() { + inner.run(); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/tictactoe/TicTacToeUtils.java b/src/main/java/de/hysky/skyblocker/utils/tictactoe/TicTacToeUtils.java new file mode 100644 index 00000000..908ba46d --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/tictactoe/TicTacToeUtils.java @@ -0,0 +1,104 @@ +package de.hysky.skyblocker.utils.tictactoe; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class TicTacToeUtils { + + public static int getBestMove(char[][] board) { + HashMap<Integer, Integer> moves = new HashMap<>(); + for (int row = 0; row < board.length; row++) { + for (int col = 0; col < board[row].length; col++) { + if (board[row][col] != '\0') continue; + board[row][col] = 'O'; + int score = alphabeta(board, Integer.MIN_VALUE, Integer.MAX_VALUE, false, 0); + board[row][col] = '\0'; + moves.put(row * 3 + col + 1, score); + } + } + return Collections.max(moves.entrySet(), Map.Entry.comparingByValue()).getKey(); + } + + public static boolean hasMovesLeft(char[][] board) { + for (char[] rows : board) { + for (char col : rows) { + if (col == '\0') return true; + } + } + return false; + } + + public static int getBoardRanking(char[][] board) { + for (int row = 0; row < 3; row++) { + if (board[row][0] == board[row][1] && board[row][0] == board[row][2]) { + if (board[row][0] == 'X') { + return -10; + } else if (board[row][0] == 'O') { + return 10; + } + } + } + + for (int col = 0; col < 3; col++) { + if (board[0][col] == board[1][col] && board[0][col] == board[2][col]) { + if (board[0][col] == 'X') { + return -10; + } else if (board[0][col] == 'O') { + return 10; + } + } + } + + if (board[0][0] == board[1][1] && board[0][0] == board[2][2]) { + if (board[0][0] == 'X') { + return -10; + } else if (board[0][0] == 'O') { + return 10; + } + } else if (board[0][2] == board[1][1] && board[0][2] == board[2][0]) { + if (board[0][2] == 'X') { + return -10; + } else if (board[0][2] == 'O') { + return 10; + } + } + + return 0; + } + public static int alphabeta(char[][] board, int alpha, int beta, boolean max, int depth) { + int score = getBoardRanking(board); + if (score == 10 || score == -10) return score; + if (!hasMovesLeft(board)) return 0; + + if (max) { + int bestScore = Integer.MIN_VALUE; + for (int row = 0; row < 3; row++) { + for (int col = 0; col < 3; col++) { + if (board[row][col] == '\0') { + board[row][col] = 'O'; + bestScore = Math.max(bestScore, alphabeta(board, alpha, beta, false, depth + 1)); + board[row][col] = '\0'; + alpha = Math.max(alpha, bestScore); + if (beta <= alpha) break; // Pruning + } + } + } + return bestScore - depth; + } else { + int bestScore = Integer.MAX_VALUE; + for (int row = 0; row < 3; row++) { + for (int col = 0; col < 3; col++) { + if (board[row][col] == '\0') { + board[row][col] = 'X'; + bestScore = Math.min(bestScore, alphabeta(board, alpha, beta, true, depth + 1)); + board[row][col] = '\0'; + beta = Math.min(beta, bestScore); + if (beta <= alpha) break; // Pruning + } + } + } + return bestScore + depth; + } + } +} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java deleted file mode 100644 index 6f4276e9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ /dev/null @@ -1,128 +0,0 @@ -package me.xmrvizzy.skyblocker; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.*; -import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; -import me.xmrvizzy.skyblocker.skyblock.dungeon.*; -import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; -import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud; -import me.xmrvizzy.skyblocker.skyblock.item.*; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import me.xmrvizzy.skyblocker.skyblock.quicknav.QuickNav; -import me.xmrvizzy.skyblocker.skyblock.rift.TheRift; -import me.xmrvizzy.skyblocker.skyblock.shortcut.Shortcuts; -import me.xmrvizzy.skyblocker.skyblock.special.SpecialEffects; -import me.xmrvizzy.skyblocker.skyblock.spidersden.Relics; -import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud; -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.utils.NEURepo; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.chat.ChatMessageListener; -import me.xmrvizzy.skyblocker.utils.discord.DiscordRPCManager; -import me.xmrvizzy.skyblocker.utils.render.culling.OcclusionCulling; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolverManager; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer; -import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; - -import java.nio.file.Path; - -/** - * Main class for Skyblocker which initializes features, registers events, and - * manages ticks. This class will be instantiated by Fabric. Do not instantiate - * this class. - */ -public class SkyblockerMod implements ClientModInitializer { - public static final String VERSION = FabricLoader.getInstance().getModContainer("skyblocker").get().getMetadata().getVersion().getFriendlyString(); - public static final String NAMESPACE = "skyblocker"; - public static final Path CONFIG_DIR = FabricLoader.getInstance().getConfigDir().resolve(NAMESPACE); - public static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); - private static SkyblockerMod INSTANCE; - public final ContainerSolverManager containerSolverManager = new ContainerSolverManager(); - public final StatusBarTracker statusBarTracker = new StatusBarTracker(); - - /** - * Do not instantiate this class. Use {@link #getInstance()} instead. - */ - @Deprecated - public SkyblockerMod() { - INSTANCE = this; - } - - public static SkyblockerMod getInstance() { - return INSTANCE; - } - - /** - * Register {@link #tick(MinecraftClient)} to - * {@link ClientTickEvents#END_CLIENT_TICK}, initialize all features, and - * schedule tick events. - */ - @Override - public void onInitializeClient() { - ClientTickEvents.END_CLIENT_TICK.register(this::tick); - Utils.init(); - HotbarSlotLock.init(); - SkyblockerConfigManager.init(); - PriceInfoTooltip.init(); - WikiLookup.init(); - ItemRegistry.init(); - NEURepo.init(); - FairySouls.init(); - Relics.init(); - BackpackPreview.init(); - QuickNav.init(); - ItemCooldowns.init(); - DwarvenHud.init(); - ChatMessageListener.init(); - Shortcuts.init(); - DiscordRPCManager.init(); - LividColor.init(); - FishingHelper.init(); - TabHud.init(); - DungeonMap.init(); - DungeonSecrets.init(); - DungeonBlaze.init(); - DungeonChestProfit.init(); - TheRift.init(); - TitleContainer.init(); - ScreenMaster.init(); - OcclusionCulling.init(); - TeleportOverlay.init(); - CustomItemNames.init(); - CustomArmorDyeColors.init(); - CustomArmorTrims.init(); - TicTacToe.init(); - QuiverWarning.init(); - SpecialEffects.init(); - ItemProtection.init(); - ItemRarityBackgrounds.init(); - containerSolverManager.init(); - statusBarTracker.init(); - Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); - Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 100); - Scheduler.INSTANCE.scheduleCyclic(TicTacToe::tick, 4); - Scheduler.INSTANCE.scheduleCyclic(LividColor::update, 10); - Scheduler.INSTANCE.scheduleCyclic(BackpackPreview::tick, 50); - Scheduler.INSTANCE.scheduleCyclic(DwarvenHud::update, 40); - Scheduler.INSTANCE.scheduleCyclic(PlayerListMgr::updateList, 20); - } - - /** - * Ticks the scheduler. Called once at the end of every client tick through - * {@link ClientTickEvents#END_CLIENT_TICK}. - * - * @param client the Minecraft client. - */ - public void tick(MinecraftClient client) { - Scheduler.INSTANCE.tick(); - MessageScheduler.INSTANCE.tick(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java deleted file mode 100644 index 8b96499d..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/MixinPlugin.java +++ /dev/null @@ -1,52 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility; - -import java.util.List; -import java.util.Set; - -import org.objectweb.asm.tree.ClassNode; -import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; -import org.spongepowered.asm.mixin.extensibility.IMixinInfo; - -import net.fabricmc.loader.api.FabricLoader; - -public class MixinPlugin implements IMixinConfigPlugin { - private static final boolean OPTIFABRIC_LOADED = FabricLoader.getInstance().isModLoaded("optifabric"); - - @Override - public void onLoad(String mixinPackage) { - //Do nothing - } - - @Override - public String getRefMapperConfig() { - return null; - } - - @Override - public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { - //OptiFabric Compatibility - if (mixinClassName.endsWith("WorldRendererMixin") && OPTIFABRIC_LOADED) return false; - - return true; - } - - @Override - public void acceptTargets(Set<String> myTargets, Set<String> otherTargets) { - //Do nothing - } - - @Override - public List<String> getMixins() { - return null; - } - - @Override - public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - //Do nothing - } - - @Override - public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { - //Do nothing - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockEmiRecipe.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockEmiRecipe.java deleted file mode 100644 index 6cf91dac..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockEmiRecipe.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.emi; - -import dev.emi.emi.api.recipe.EmiCraftingRecipe; -import dev.emi.emi.api.recipe.EmiRecipeCategory; -import dev.emi.emi.api.stack.Comparison; -import dev.emi.emi.api.stack.EmiIngredient; -import dev.emi.emi.api.stack.EmiStack; -import dev.emi.emi.api.widget.WidgetHolder; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import me.xmrvizzy.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -public class SkyblockEmiRecipe extends EmiCraftingRecipe { - private final String craftText; - - public SkyblockEmiRecipe(SkyblockCraftingRecipe recipe) { - super(recipe.getGrid().stream().map(EmiStack::of).map(EmiIngredient.class::cast).toList(), EmiStack.of(recipe.getResult()).comparison(Comparison.compareNbt()), Identifier.of("skyblock", ItemRegistry.getInternalName(recipe.getResult()).toLowerCase().replace(';', '_'))); - this.craftText = recipe.getCraftText(); - } - - @Override - public EmiRecipeCategory getCategory() { - return SkyblockerEMIPlugin.SKYBLOCK; - } - - @Override - public int getDisplayHeight() { - return super.getDisplayHeight() + (craftText.isEmpty() ? 0 : 10); - } - - @Override - public void addWidgets(WidgetHolder widgets) { - super.addWidgets(widgets); - widgets.addText(Text.of(craftText), 59 - MinecraftClient.getInstance().textRenderer.getWidth(craftText) / 2, 55, 0xFFFFFF, true); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java deleted file mode 100644 index 1df75de0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/emi/SkyblockerEMIPlugin.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.emi; - -import dev.emi.emi.api.EmiPlugin; -import dev.emi.emi.api.EmiRegistry; -import dev.emi.emi.api.recipe.EmiRecipeCategory; -import dev.emi.emi.api.render.EmiTexture; -import dev.emi.emi.api.stack.EmiStack; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import me.xmrvizzy.skyblocker.utils.ItemUtils; -import net.minecraft.item.Items; -import net.minecraft.util.Identifier; - -/** - * EMI integration - */ -public class SkyblockerEMIPlugin implements EmiPlugin { - public static final Identifier SIMPLIFIED_TEXTURES = new Identifier("emi", "textures/gui/widgets.png"); - // TODO: Custom simplified texture for Skyblock - public static final EmiRecipeCategory SKYBLOCK = new EmiRecipeCategory(new Identifier(SkyblockerMod.NAMESPACE, "skyblock"), EmiStack.of(ItemUtils.getSkyblockerStack()), new EmiTexture(SIMPLIFIED_TEXTURES, 240, 240, 16, 16)); - - @Override - public void register(EmiRegistry registry) { - ItemRegistry.getItemsStream().map(EmiStack::of).forEach(registry::addEmiStack); - registry.addCategory(SKYBLOCK); - registry.addWorkstation(SKYBLOCK, EmiStack.of(Items.CRAFTING_TABLE)); - ItemRegistry.getRecipesStream().map(SkyblockEmiRecipe::new).forEach(registry::addRecipe); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/modmenu/ModMenuEntry.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/modmenu/ModMenuEntry.java deleted file mode 100644 index 5a3f4504..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/modmenu/ModMenuEntry.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.modmenu; - -import com.terraformersmc.modmenu.api.ConfigScreenFactory; -import com.terraformersmc.modmenu.api.ModMenuApi; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; - -@Environment(EnvType.CLIENT) -public class ModMenuEntry implements ModMenuApi { - @Override - public ConfigScreenFactory<?> getModConfigScreenFactory() { - return SkyblockerConfigManager::createGUI; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCategory.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCategory.java deleted file mode 100644 index 14c61d23..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCategory.java +++ /dev/null @@ -1,84 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.rei; - -import com.google.common.collect.Lists; -import me.shedaniel.math.Point; -import me.shedaniel.math.Rectangle; -import me.shedaniel.rei.api.client.gui.Renderer; -import me.shedaniel.rei.api.client.gui.widgets.Label; -import me.shedaniel.rei.api.client.gui.widgets.Slot; -import me.shedaniel.rei.api.client.gui.widgets.Widget; -import me.shedaniel.rei.api.client.gui.widgets.Widgets; -import me.shedaniel.rei.api.client.registry.display.DisplayCategory; -import me.shedaniel.rei.api.common.category.CategoryIdentifier; -import me.shedaniel.rei.api.common.entry.EntryIngredient; -import me.shedaniel.rei.api.common.util.EntryStacks; -import me.xmrvizzy.skyblocker.utils.ItemUtils; -import net.minecraft.text.Text; - -import java.util.ArrayList; -import java.util.List; - -/** - * Skyblock recipe category class for REI - */ -public class SkyblockCategory implements DisplayCategory<SkyblockCraftingDisplay> { - @Override - public CategoryIdentifier<SkyblockCraftingDisplay> getCategoryIdentifier() { - return SkyblockerREIClientPlugin.SKYBLOCK; - } - - @Override - public Text getTitle() { - return Text.translatable("emi.category.skyblocker.skyblock"); - } - - @Override - public Renderer getIcon() { - return EntryStacks.of(ItemUtils.getSkyblockerStack()); - } - - @Override - public int getDisplayHeight() { - return 73; - } - - /** - * Draws display for SkyblockCraftingDisplay - * - * @param display the display - * @param bounds the bounds of the display, configurable with overriding the width, height methods. - */ - @Override - public List<Widget> setupDisplay(SkyblockCraftingDisplay display, Rectangle bounds) { - List<Widget> out = new ArrayList<>(); - out.add(Widgets.createRecipeBase(bounds)); - - Point startPoint; - if (!display.getCraftText().isEmpty() && display.getCraftText() != null) { - startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 31); - } - else { - startPoint = new Point(bounds.getCenterX() - 58, bounds.getCenterY() - 26); - } - Point resultPoint = new Point(startPoint.x + 95, startPoint.y + 19); - out.add(Widgets.createArrow(new Point(startPoint.x + 60, startPoint.y + 18))); - out.add(Widgets.createResultSlotBackground(resultPoint)); - - // Generate Slots - List<EntryIngredient> input = display.getInputEntries(); - List<Slot> slots = Lists.newArrayList(); - for (int y = 0; y < 3; y++) - for (int x = 0; x < 3; x++) - slots.add(Widgets.createSlot(new Point(startPoint.x + 1 + x * 18, startPoint.y + 1 + y * 18)).markInput()); - for (int i = 0; i < input.size(); i++) { - slots.get(i).entries(input.get(i)).markInput(); - } - out.addAll(slots); - out.add(Widgets.createSlot(resultPoint).entries(display.getOutputEntries().get(0)).disableBackground().markOutput()); - - // Add craftingText Label - Label craftTextLabel = Widgets.createLabel(new Point(bounds.getCenterX(), startPoint.y + 55), Text.of(display.getCraftText())); - out.add(craftTextLabel); - return out; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java deleted file mode 100644 index 35b5c886..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplay.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.rei; - - -import me.shedaniel.rei.api.common.category.CategoryIdentifier; -import me.shedaniel.rei.api.common.display.SimpleGridMenuDisplay; -import me.shedaniel.rei.api.common.display.basic.BasicDisplay; -import me.shedaniel.rei.api.common.entry.EntryIngredient; - -import java.util.List; - -/** - * Skyblock Crafting Recipe display class for REI - */ -public class SkyblockCraftingDisplay extends BasicDisplay implements SimpleGridMenuDisplay { - private final String craftText; - - public SkyblockCraftingDisplay(List<EntryIngredient> input, List<EntryIngredient> output, String craftText) { - super(input, output); - this.craftText = craftText; - } - - public String getCraftText() { - return craftText; - } - - @Override - public int getWidth() { - return 3; - } - - @Override - public int getHeight() { - return 3; - } - - @Override - public CategoryIdentifier<?> getCategoryIdentifier() { - return SkyblockerREIClientPlugin.SKYBLOCK; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java deleted file mode 100644 index 370e15dc..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockCraftingDisplayGenerator.java +++ /dev/null @@ -1,65 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.rei; - -import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; -import me.shedaniel.rei.api.common.entry.EntryIngredient; -import me.shedaniel.rei.api.common.entry.EntryStack; -import me.shedaniel.rei.api.common.util.EntryStacks; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import me.xmrvizzy.skyblocker.skyblock.itemlist.SkyblockCraftingRecipe; -import net.minecraft.item.ItemStack; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -public class SkyblockCraftingDisplayGenerator implements DynamicDisplayGenerator<SkyblockCraftingDisplay> { - - @Override - public Optional<List<SkyblockCraftingDisplay>> getRecipeFor(EntryStack<?> entry) { - if (!(entry.getValue() instanceof ItemStack)) return Optional.empty(); - EntryStack<ItemStack> inputItem = EntryStacks.of((ItemStack) entry.getValue()); - List<SkyblockCraftingRecipe> filteredRecipes = ItemRegistry.getRecipesStream() - .filter(recipe -> ItemRegistry.getInternalName(recipe.getResult()).equals(ItemRegistry.getInternalName(inputItem.getValue()))) - .toList(); - - return Optional.of(generateDisplays(filteredRecipes)); - } - - @Override - public Optional<List<SkyblockCraftingDisplay>> getUsageFor(EntryStack<?> entry) { - if (!(entry.getValue() instanceof ItemStack)) return Optional.empty(); - EntryStack<ItemStack> inputItem = EntryStacks.of((ItemStack) entry.getValue()); - List<SkyblockCraftingRecipe> filteredRecipes = ItemRegistry.getRecipesStream() - .filter(recipe -> { - for (ItemStack item : recipe.getGrid()) { - if(!ItemRegistry.getInternalName(item).isEmpty() && ItemRegistry.getInternalName(item).equals(ItemRegistry.getInternalName(inputItem.getValue()))) - return true; - } - return false; - }) - .toList(); - return Optional.of(generateDisplays(filteredRecipes)); - } - - /** - * Generate Displays from a list of recipes - */ - private List<SkyblockCraftingDisplay> generateDisplays(List<SkyblockCraftingRecipe> recipes) { - List<SkyblockCraftingDisplay> displays = new ArrayList<>(); - for (SkyblockCraftingRecipe recipe : recipes) { - List<EntryIngredient> inputs = new ArrayList<>(); - List<EntryIngredient> outputs = new ArrayList<>(); - - ArrayList<EntryStack<ItemStack>> inputEntryStacks = new ArrayList<>(); - recipe.getGrid().forEach((item) -> inputEntryStacks.add(EntryStacks.of(item))); - - for (EntryStack<ItemStack> entryStack : inputEntryStacks) { - inputs.add(EntryIngredient.of(entryStack)); - } - outputs.add(EntryIngredient.of(EntryStacks.of(recipe.getResult()))); - - displays.add(new SkyblockCraftingDisplay(inputs, outputs, recipe.getCraftText())); - } - return displays; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java b/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java deleted file mode 100644 index 32f0bd85..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/compatibility/rei/SkyblockerREIClientPlugin.java +++ /dev/null @@ -1,34 +0,0 @@ -package me.xmrvizzy.skyblocker.compatibility.rei; - -import me.shedaniel.rei.api.client.plugins.REIClientPlugin; -import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; -import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; -import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; -import me.shedaniel.rei.api.common.category.CategoryIdentifier; -import me.shedaniel.rei.api.common.util.EntryStacks; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import net.minecraft.item.Items; - -/** - * REI integration - */ -public class SkyblockerREIClientPlugin implements REIClientPlugin { - public static final CategoryIdentifier<SkyblockCraftingDisplay> SKYBLOCK = CategoryIdentifier.of(SkyblockerMod.NAMESPACE, "skyblock"); - - @Override - public void registerCategories(CategoryRegistry categoryRegistry) { - categoryRegistry.addWorkstations(SKYBLOCK, EntryStacks.of(Items.CRAFTING_TABLE)); - categoryRegistry.add(new SkyblockCategory()); - } - - @Override - public void registerDisplays(DisplayRegistry displayRegistry) { - displayRegistry.registerDisplayGenerator(SKYBLOCK, new SkyblockCraftingDisplayGenerator()); - } - - @Override - public void registerEntries(EntryRegistry entryRegistry) { - entryRegistry.addEntries(ItemRegistry.getItemsStream().map(EntryStacks::of).toList()); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java b/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java deleted file mode 100644 index 552ed091..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/ConfigUtils.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.xmrvizzy.skyblocker.config; - -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.controller.BooleanControllerBuilder; -import dev.isxander.yacl3.api.controller.EnumControllerBuilder; -import dev.isxander.yacl3.api.controller.ValueFormatter; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.apache.commons.lang3.StringUtils; - -import java.util.function.Function; - -public class ConfigUtils { - public static final Function<Formatting, String> FORMATTING_TO_STRING = formatting -> StringUtils.capitalize(formatting.getName().replaceAll("_", " ")); - public static final ValueFormatter<Float> FLOAT_TWO_FORMATTER = value -> Text.literal(String.format("%,.2f", value).replaceAll("[\u00a0\u202F]", " ")); - - public static BooleanControllerBuilder createBooleanController(Option<Boolean> opt) { - return BooleanControllerBuilder.create(opt).yesNoFormatter().coloured(true); - } - - @SuppressWarnings("unchecked") - public static <E extends Enum<E>> EnumControllerBuilder<E> createEnumCyclingListController(Option<E> opt) { - return EnumControllerBuilder.create(opt).enumClass((Class<E>) opt.binding().defaultValue().getClass()); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java deleted file mode 100644 index 8e014124..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ /dev/null @@ -1,787 +0,0 @@ -package me.xmrvizzy.skyblocker.config; - -import dev.isxander.yacl3.config.v2.api.SerialEntry; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import net.minecraft.client.resource.language.I18n; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.ArrayList; -import java.util.List; - -public class SkyblockerConfig { - @SerialEntry - public int version = 1; - - @SerialEntry - public General general = new General(); - - @SerialEntry - public Locations locations = new Locations(); - - @SerialEntry - public Slayer slayer = new Slayer(); - - @SerialEntry - public QuickNav quickNav = new QuickNav(); - - @SerialEntry - public Messages messages = new Messages(); - - @SerialEntry - public RichPresence richPresence = new RichPresence(); - - 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"); - - @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"); - } - - public static class QuickNavItem { - public QuickNavItem(Boolean render, ItemData itemData, String uiTitle, String clickEvent) { - this.render = render; - this.item = itemData; - this.clickEvent = clickEvent; - this.uiTitle = uiTitle; - } - - @SerialEntry - public Boolean render; - - @SerialEntry - public ItemData item; - - @SerialEntry - public String uiTitle; - - @SerialEntry - public String clickEvent; - } - - public static class ItemData { - public ItemData(String itemName, int count, String nbt) { - this.itemName = itemName; - this.count = count; - this.nbt = nbt; - } - - public ItemData(String itemName) { - this.itemName = itemName; - this.count = 1; - this.nbt = ""; - } - - @SerialEntry - public String itemName; - - @SerialEntry - public int count; - - @SerialEntry - public String nbt; - } - - public static class General { - @SerialEntry - public boolean acceptReparty = true; - - @SerialEntry - public boolean backpackPreviewWithoutShift = false; - - @SerialEntry - public boolean compactorDeletorPreview = true; - - @SerialEntry - public boolean hideEmptyTooltips = true; - - @SerialEntry - public boolean hideStatusEffectOverlay = false; - - @SerialEntry - public TabHudConf tabHud = new TabHudConf(); - - @SerialEntry - public Bars bars = new Bars(); - - @SerialEntry - public Experiments experiments = new Experiments(); - - @SerialEntry - public Fishing fishing = new Fishing(); - - @SerialEntry - public FairySouls fairySouls = new FairySouls(); - - @SerialEntry - public ItemCooldown itemCooldown = new ItemCooldown(); - - @SerialEntry - public Shortcuts shortcuts = new Shortcuts(); - - @SerialEntry - public QuiverWarning quiverWarning = new QuiverWarning(); - - @SerialEntry - public ItemList itemList = new ItemList(); - - @SerialEntry - public ItemTooltip itemTooltip = new ItemTooltip(); - - @SerialEntry - public ItemInfoDisplay itemInfoDisplay = new ItemInfoDisplay(); - - @SerialEntry - public SpecialEffects specialEffects = new SpecialEffects(); - - @SerialEntry - public Hitbox hitbox = new Hitbox(); - - @SerialEntry - public TitleContainer titleContainer = new TitleContainer(); - - @SerialEntry - public TeleportOverlay teleportOverlay = new TeleportOverlay(); - - @SerialEntry - public List<Integer> lockedSlots = new ArrayList<>(); - - @SerialEntry - public ObjectOpenHashSet<String> protectedItems = new ObjectOpenHashSet<>(); - - @SerialEntry - public Object2ObjectOpenHashMap<String, Text> customItemNames = new Object2ObjectOpenHashMap<>(); - - @SerialEntry - public Object2IntOpenHashMap<String> customDyeColors = new Object2IntOpenHashMap<>(); - - @SerialEntry - public Object2ObjectOpenHashMap<String, CustomArmorTrims.ArmorTrimId> customArmorTrims = new Object2ObjectOpenHashMap<>(); - } - - public static class TabHudConf { - @SerialEntry - public boolean tabHudEnabled = true; - - @SerialEntry - public int tabHudScale = 100; - - @SerialEntry - public boolean plainPlayerNames = false; - - @SerialEntry - public NameSorting nameSorting = NameSorting.DEFAULT; - } - - public enum NameSorting { - DEFAULT, ALPHABETICAL; - - @Override - public String toString() { - return switch (this) { - case DEFAULT -> "Default"; - case ALPHABETICAL -> "Alphabetical"; - }; - } - } - - public static class Bars { - @SerialEntry - public boolean enableBars = true; - - @SerialEntry - public BarPositions barPositions = new BarPositions(); - } - - public static class BarPositions { - @SerialEntry - public BarPosition healthBarPosition = BarPosition.LAYER1; - - @SerialEntry - public BarPosition manaBarPosition = BarPosition.LAYER1; - - @SerialEntry - public BarPosition defenceBarPosition = BarPosition.LAYER1; - - @SerialEntry - public BarPosition experienceBarPosition = BarPosition.LAYER1; - - } - - public enum BarPosition { - LAYER1, LAYER2, RIGHT, NONE; - - @Override - public String toString() { - return I18n.translate("text.autoconfig.skyblocker.option.general.bars.barpositions." + name()); - } - - public int toInt() { - return switch (this) { - case LAYER1 -> 0; - case LAYER2 -> 1; - case RIGHT -> 2; - case NONE -> -1; - }; - } - } - - public static class Experiments { - @SerialEntry - public boolean enableChronomatronSolver = true; - - @SerialEntry - public boolean enableSuperpairsSolver = true; - - @SerialEntry - public boolean enableUltrasequencerSolver = true; - } - - public static class Fishing { - @SerialEntry - public boolean enableFishingHelper = true; - } - - public static class FairySouls { - @SerialEntry - public boolean enableFairySoulsHelper = false; - - @SerialEntry - public boolean highlightFoundSouls = true; - - @SerialEntry - public boolean highlightOnlyNearbySouls = false; - } - - public static class ItemCooldown { - @SerialEntry - public boolean enableItemCooldowns = true; - } - - public static class Shortcuts { - @SerialEntry - public boolean enableShortcuts = true; - - @SerialEntry - public boolean enableCommandShortcuts = true; - - @SerialEntry - public boolean enableCommandArgShortcuts = true; - } - - public static class QuiverWarning { - @SerialEntry - public boolean enableQuiverWarning = true; - - @SerialEntry - public boolean enableQuiverWarningInDungeons = true; - - @SerialEntry - public boolean enableQuiverWarningAfterDungeon = true; - } - - public static class Hitbox { - @SerialEntry - public boolean oldFarmlandHitbox = true; - - @SerialEntry - public boolean oldLeverHitbox = false; - } - - public static class TitleContainer { - @SerialEntry - public float titleContainerScale = 100; - - @SerialEntry - public int x = 540; - - @SerialEntry - public int y = 10; - - @SerialEntry - public Direction direction = Direction.HORIZONTAL; - - @SerialEntry - public Alignment alignment = Alignment.MIDDLE; - } - - public static class TeleportOverlay { - @SerialEntry - public boolean enableTeleportOverlays = true; - - @SerialEntry - public boolean enableWeirdTransmission = true; - - @SerialEntry - public boolean enableInstantTransmission = true; - - @SerialEntry - public boolean enableEtherTransmission = true; - - @SerialEntry - public boolean enableSinrecallTransmission = true; - - @SerialEntry - public boolean enableWitherImpact = true; - } - - public enum Direction { - HORIZONTAL, VERTICAL; - - @Override - public String toString() { - return switch (this) { - case HORIZONTAL -> "Horizontal"; - case VERTICAL -> "Vertical"; - }; - } - } - - public enum Alignment { - LEFT, RIGHT, MIDDLE; - - @Override - public String toString() { - return switch (this) { - case LEFT -> "Left"; - case RIGHT -> "Right"; - case MIDDLE -> "Middle"; - }; - } - } - - public static class RichPresence { - @SerialEntry - public boolean enableRichPresence = false; - - @SerialEntry - public Info info = Info.LOCATION; - - @SerialEntry - public boolean cycleMode = false; - - @SerialEntry - public String customMessage = "Playing Skyblock"; - } - - public static class ItemList { - @SerialEntry - public boolean enableItemList = true; - } - - public enum Average { - ONE_DAY, THREE_DAY, BOTH; - - @Override - public String toString() { - return I18n.translate("text.autoconfig.skyblocker.option.general.itemTooltip.avg." + name()); - } - } - - public static class ItemTooltip { - @SerialEntry - public boolean enableNPCPrice = true; - - @SerialEntry - public boolean enableMotesPrice = true; - - @SerialEntry - public boolean enableAvgBIN = true; - - @SerialEntry - public Average avg = Average.THREE_DAY; - - @SerialEntry - public boolean enableLowestBIN = true; - - @SerialEntry - public boolean enableBazaarPrice = true; - - @SerialEntry - public boolean enableMuseumDate = true; - } - - public static class ItemInfoDisplay { - @SerialEntry - public boolean attributeShardInfo = true; - - @SerialEntry - public boolean itemRarityBackgrounds = false; - - @SerialEntry - public float itemRarityBackgroundsOpacity = 1f; - } - - public static class SpecialEffects { - @SerialEntry - public boolean rareDungeonDropEffects = true; - } - - public static class Locations { - @SerialEntry - public Barn barn = new Barn(); - - @SerialEntry - public Dungeons dungeons = new Dungeons(); - - @SerialEntry - public DwarvenMines dwarvenMines = new DwarvenMines(); - - @SerialEntry - public Rift rift = new Rift(); - - @SerialEntry - public SpidersDen spidersDen = new SpidersDen(); - } - - public static class Dungeons { - @SerialEntry - public SecretWaypoints secretWaypoints = new SecretWaypoints(); - - @SerialEntry - public DungeonChestProfit dungeonChestProfit = new DungeonChestProfit(); - - @SerialEntry - public boolean croesusHelper = true; - - @SerialEntry - public boolean enableMap = true; - - @SerialEntry - public float mapScaling = 1f; - - @SerialEntry - public int mapX = 2; - - @SerialEntry - public int mapY = 2; - - @SerialEntry - public boolean starredMobGlow = true; - - @SerialEntry - public boolean solveThreeWeirdos = true; - - @SerialEntry - public boolean blazesolver = true; - - @SerialEntry - public boolean solveTrivia = true; - - @SerialEntry - public boolean solveTicTacToe = true; - - @SerialEntry - public LividColor lividColor = new LividColor(); - - @SerialEntry - public Terminals terminals = new Terminals(); - } - - public static class SecretWaypoints { - @SerialEntry - public boolean enableSecretWaypoints = true; - - @SerialEntry - public boolean noInitSecretWaypoints = false; - - @SerialEntry - public boolean enableEntranceWaypoints = true; - - @SerialEntry - public boolean enableSuperboomWaypoints = true; - - @SerialEntry - public boolean enableChestWaypoints = true; - - @SerialEntry - public boolean enableItemWaypoints = true; - - @SerialEntry - public boolean enableBatWaypoints = true; - - @SerialEntry - public boolean enableWitherWaypoints = true; - - @SerialEntry - public boolean enableLeverWaypoints = true; - - @SerialEntry - public boolean enableFairySoulWaypoints = true; - - @SerialEntry - public boolean enableStonkWaypoints = true; - - @SerialEntry - public boolean enableDefaultWaypoints = true; - } - - public static class DungeonChestProfit { - @SerialEntry - public boolean enableProfitCalculator = true; - - @SerialEntry - public boolean includeKismet = false; - - @SerialEntry - public boolean includeEssence = true; - - @SerialEntry - public int neutralThreshold = 1000; - - @SerialEntry - public Formatting neutralColor = Formatting.DARK_GRAY; - - @SerialEntry - public Formatting profitColor = Formatting.DARK_GREEN; - - @SerialEntry - public Formatting lossColor = Formatting.RED; - - @SerialEntry - public Formatting incompleteColor = Formatting.BLUE; - - } - - public static class LividColor { - @SerialEntry - public boolean enableLividColor = true; - - @SerialEntry - public String lividColorText = "The livid color is [color]"; - } - - public static class Terminals { - @SerialEntry - public boolean solveColor = true; - - @SerialEntry - public boolean solveOrder = true; - - @SerialEntry - public boolean solveStartsWith = true; - } - - public static class DwarvenMines { - @SerialEntry - public boolean enableDrillFuel = true; - - @SerialEntry - public boolean solveFetchur = true; - - @SerialEntry - public boolean solvePuzzler = true; - - @SerialEntry - public DwarvenHud dwarvenHud = new DwarvenHud(); - } - - public static class DwarvenHud { - @SerialEntry - public boolean enabled = true; - - @SerialEntry - public DwarvenHudStyle style = DwarvenHudStyle.SIMPLE; - - @SerialEntry - public boolean enableBackground = true; - - @SerialEntry - public int x = 10; - - @SerialEntry - public int y = 10; - } - - public enum DwarvenHudStyle { - SIMPLE, FANCY, CLASSIC; - - @Override - public String toString() { - return switch (this) { - case SIMPLE -> "Simple"; - case FANCY -> "Fancy"; - case CLASSIC -> "Classic"; - }; - } - } - - public static class Barn { - @SerialEntry - public boolean solveHungryHiker = true; - - @SerialEntry - public boolean solveTreasureHunter = true; - } - - public static class Rift { - @SerialEntry - public boolean mirrorverseWaypoints = true; - - @SerialEntry - public int mcGrubberStacks = 0; - } - - public static class SpidersDen { - @SerialEntry - public Relics relics = new Relics(); - } - - public static class Relics { - @SerialEntry - public boolean enableRelicsHelper = false; - - @SerialEntry - public boolean highlightFoundRelics = true; - } - - public static class Slayer { - @SerialEntry - public VampireSlayer vampireSlayer = new VampireSlayer(); - } - - public static class VampireSlayer { - @SerialEntry - public boolean enableEffigyWaypoints = true; - - @SerialEntry - public boolean compactEffigyWaypoints; - - @SerialEntry - public int effigyUpdateFrequency = 5; - - @SerialEntry - public boolean enableHolyIceIndicator = true; - - @SerialEntry - public int holyIceIndicatorTickDelay = 10; - - @SerialEntry - public int holyIceUpdateFrequency = 5; - - @SerialEntry - public boolean enableHealingMelonIndicator = true; - - @SerialEntry - public float healingMelonHealthThreshold = 4f; - - @SerialEntry - public boolean enableSteakStakeIndicator = true; - - @SerialEntry - public int steakStakeUpdateFrequency = 5; - - @SerialEntry - public boolean enableManiaIndicator = true; - - @SerialEntry - public int maniaUpdateFrequency = 5; - } - - public static class Messages { - @SerialEntry - public ChatFilterResult hideAbility = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideHeal = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideAOTE = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideImplosion = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideMoltenWave = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideAds = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideTeleportPad = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideCombo = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideAutopet = ChatFilterResult.PASS; - - @SerialEntry - public ChatFilterResult hideShowOff = ChatFilterResult.PASS; - - @SerialEntry - public boolean hideMana = false; - } - - public enum Info { - PURSE, BITS, LOCATION; - - @Override - public String toString() { - return I18n.translate("text.autoconfig.skyblocker.option.richPresence.info." + name()); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfigManager.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfigManager.java deleted file mode 100644 index eb756641..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfigManager.java +++ /dev/null @@ -1,86 +0,0 @@ -package me.xmrvizzy.skyblocker.config; - -import java.lang.StackWalker.Option; -import java.nio.file.Path; - -import com.google.gson.FieldNamingPolicy; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; - -import dev.isxander.yacl3.api.YetAnotherConfigLib; -import dev.isxander.yacl3.config.v2.api.ConfigClassHandler; -import dev.isxander.yacl3.config.v2.api.serializer.GsonConfigSerializerBuilder; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.categories.DiscordRPCCategory; -import me.xmrvizzy.skyblocker.config.categories.DungeonsCategory; -import me.xmrvizzy.skyblocker.config.categories.DwarvenMinesCategory; -import me.xmrvizzy.skyblocker.config.categories.GeneralCategory; -import me.xmrvizzy.skyblocker.config.categories.LocationsCategory; -import me.xmrvizzy.skyblocker.config.categories.MessageFilterCategory; -import me.xmrvizzy.skyblocker.config.categories.QuickNavigationCategory; -import me.xmrvizzy.skyblocker.config.categories.SlayersCategory; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -public class SkyblockerConfigManager { - private static final Path PATH = FabricLoader.getInstance().getConfigDir().resolve("skyblocker.json"); - private static final ConfigClassHandler<SkyblockerConfig> HANDLER = ConfigClassHandler.createBuilder(SkyblockerConfig.class) - .serializer(config -> GsonConfigSerializerBuilder.create(config) - .setPath(PATH) - .setJson5(false) - .appendGsonBuilder(builder -> builder - .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY) - .registerTypeHierarchyAdapter(Identifier.class, new Identifier.Serializer())) - .build()) - .build(); - - public static SkyblockerConfig get() { - return HANDLER.instance(); - } - - /** - * This method is caller sensitive and can only be called by the mod initializer, - * this is enforced. - */ - public static void init() { - if (StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE).getCallerClass() != SkyblockerMod.class) { - throw new RuntimeException("Skyblocker: Called config init from an illegal place!"); - } - - HANDLER.load(); - ClientCommandRegistrationCallback.EVENT.register(((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE).then(optionsLiteral("config")).then(optionsLiteral("options"))))); - } - - public static void save() { - HANDLER.save(); - } - - public static Screen createGUI(Screen parent) { - return YetAnotherConfigLib.create(HANDLER, (defaults, config, builder) -> builder - .title(Text.translatable("text.autoconfig.skyblocker.title")) - .category(GeneralCategory.create(defaults, config)) - .category(DungeonsCategory.create(defaults, config)) - .category(DwarvenMinesCategory.create(defaults, config)) - .category(LocationsCategory.create(defaults, config)) - .category(SlayersCategory.create(defaults, config)) - .category(QuickNavigationCategory.create(defaults, config)) - .category(MessageFilterCategory.create(defaults, config)) - .category(DiscordRPCCategory.create(defaults, config))).generateScreen(parent); - } - - /** - * Registers an options command with the given name. Used for registering both options and config as valid commands. - * - * @param name the name of the command node - * @return the command builder - */ - private static LiteralArgumentBuilder<FabricClientCommandSource> optionsLiteral(String name) { - // Don't immediately open the next screen as it will be closed by ChatScreen right after this command is executed - return ClientCommandManager.literal(name).executes(Scheduler.queueOpenScreenCommand(() -> createGUI(null))); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DiscordRPCCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DiscordRPCCategory.java deleted file mode 100644 index 1e35bc32..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DiscordRPCCategory.java +++ /dev/null @@ -1,49 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionDescription; -import dev.isxander.yacl3.api.controller.StringControllerBuilder; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import net.minecraft.text.Text; - -public class DiscordRPCCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.category.richPresence")) - - //Uncategorized Options - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.enableRichPresence")) - .binding(defaults.richPresence.enableRichPresence, - () -> config.richPresence.enableRichPresence, - newValue -> config.richPresence.enableRichPresence = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<SkyblockerConfig.Info>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.info")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.richPresence.info.@Tooltip"))) - .binding(defaults.richPresence.info, - () -> config.richPresence.info, - newValue -> config.richPresence.info = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.cycleMode")) - .binding(defaults.richPresence.cycleMode, - () -> config.richPresence.cycleMode, - newValue -> config.richPresence.cycleMode = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.richPresence.customMessage")) - .binding(defaults.richPresence.customMessage, - () -> config.richPresence.customMessage, - newValue -> config.richPresence.customMessage = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java deleted file mode 100644 index 2d641ac1..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DungeonsCategory.java +++ /dev/null @@ -1,316 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -import dev.isxander.yacl3.api.ButtonOption; -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionDescription; -import dev.isxander.yacl3.api.OptionFlag; -import dev.isxander.yacl3.api.OptionGroup; -import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.StringControllerBuilder; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import me.xmrvizzy.skyblocker.config.controllers.EnumDropdownControllerBuilder; -import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMapConfigScreen; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class DungeonsCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons")) - - //Dungeon Secret Waypoints - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSecretWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableSecretWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableSecretWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableSecretWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.noInitSecretWaypoints")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.noInitSecretWaypoints.@Tooltip"))) - .binding(defaults.locations.dungeons.secretWaypoints.noInitSecretWaypoints, - () -> config.locations.dungeons.secretWaypoints.noInitSecretWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.noInitSecretWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .flag(OptionFlag.GAME_RESTART) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableEntranceWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableEntranceWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableEntranceWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableEntranceWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSuperboomWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableSuperboomWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableSuperboomWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableSuperboomWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableChestWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableChestWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableChestWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableChestWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableItemWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableItemWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableItemWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableItemWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableBatWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableBatWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableBatWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableBatWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableWitherWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableWitherWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableWitherWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableWitherWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableLeverWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableLeverWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableLeverWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableLeverWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableFairySoulWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableFairySoulWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableFairySoulWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableFairySoulWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableStonkWaypoints")) - .binding(defaults.locations.dungeons.secretWaypoints.enableStonkWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableStonkWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableStonkWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableDefaultWaypoints.@Tooltip"))) - .binding(defaults.locations.dungeons.secretWaypoints.enableDefaultWaypoints, - () -> config.locations.dungeons.secretWaypoints.enableDefaultWaypoints, - newValue -> config.locations.dungeons.secretWaypoints.enableDefaultWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.enableProfitCalculator.@Tooltip"))) - .binding(defaults.locations.dungeons.dungeonChestProfit.enableProfitCalculator, - () -> config.locations.dungeons.dungeonChestProfit.enableProfitCalculator, - newValue -> config.locations.dungeons.dungeonChestProfit.enableProfitCalculator = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeKismet.@Tooltip"))) - .binding(defaults.locations.dungeons.dungeonChestProfit.includeKismet, - () -> config.locations.dungeons.dungeonChestProfit.includeKismet, - newValue -> config.locations.dungeons.dungeonChestProfit.includeKismet = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.includeEssence.@Tooltip"))) - .binding(defaults.locations.dungeons.dungeonChestProfit.includeEssence, - () -> config.locations.dungeons.dungeonChestProfit.includeEssence, - newValue -> config.locations.dungeons.dungeonChestProfit.includeEssence = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralThreshold.@Tooltip"))) - .binding(defaults.locations.dungeons.dungeonChestProfit.neutralThreshold, - () -> config.locations.dungeons.dungeonChestProfit.neutralThreshold, - newValue -> config.locations.dungeons.dungeonChestProfit.neutralThreshold = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<Formatting>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.neutralColor")) - .binding(defaults.locations.dungeons.dungeonChestProfit.neutralColor, - () -> config.locations.dungeons.dungeonChestProfit.neutralColor, - newValue -> config.locations.dungeons.dungeonChestProfit.neutralColor = newValue) - .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) - .build()) - .option(Option.<Formatting>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.profitColor")) - .binding(defaults.locations.dungeons.dungeonChestProfit.profitColor, - () -> config.locations.dungeons.dungeonChestProfit.profitColor, - newValue -> config.locations.dungeons.dungeonChestProfit.profitColor = newValue) - .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) - .build()) - .option(Option.<Formatting>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.lossColor")) - .binding(defaults.locations.dungeons.dungeonChestProfit.lossColor, - () -> config.locations.dungeons.dungeonChestProfit.lossColor, - newValue -> config.locations.dungeons.dungeonChestProfit.lossColor = newValue) - .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) - .build()) - .option(Option.<Formatting>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.dungeonChestProfit.incompleteColor.@Tooltip"))) - .binding(defaults.locations.dungeons.dungeonChestProfit.incompleteColor, - () -> config.locations.dungeons.dungeonChestProfit.incompleteColor, - newValue -> config.locations.dungeons.dungeonChestProfit.incompleteColor = newValue) - .controller(EnumDropdownControllerBuilder.getFactory(ConfigUtils.FORMATTING_TO_STRING)) - .build()) - .build()) - - //Others - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.croesusHelper.@Tooltip"))) - .binding(defaults.locations.dungeons.croesusHelper, - () -> config.locations.dungeons.croesusHelper, - newValue -> config.locations.dungeons.croesusHelper = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.enableMap")) - .binding(defaults.locations.dungeons.enableMap, - () -> config.locations.dungeons.enableMap, - newValue -> config.locations.dungeons.enableMap = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(ButtonOption.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapScreen")) - .text(Text.translatable("text.skyblocker.open")) - .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new DungeonMapConfigScreen(screen))) - .build()) - .option(Option.<Float>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapScaling")) - .binding(defaults.locations.dungeons.mapScaling, - () -> config.locations.dungeons.mapScaling, - newValue -> config.locations.dungeons.mapScaling = newValue) - .controller(FloatFieldControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapX")) - .binding(defaults.locations.dungeons.mapX, - () -> config.locations.dungeons.mapX, - newValue -> config.locations.dungeons.mapX = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.mapY")) - .binding(defaults.locations.dungeons.mapY, - () -> config.locations.dungeons.mapY, - newValue -> config.locations.dungeons.mapY = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.starredMobGlow.@Tooltip"))) - .binding(defaults.locations.dungeons.starredMobGlow, - () -> config.locations.dungeons.starredMobGlow, - newValue -> config.locations.dungeons.starredMobGlow = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveThreeWeirdos")) - .binding(defaults.locations.dungeons.solveThreeWeirdos, - () -> config.locations.dungeons.solveThreeWeirdos, - newValue -> config.locations.dungeons.solveThreeWeirdos = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazesolver")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.blazesolver.@Tooltip"))) - .binding(defaults.locations.dungeons.blazesolver, - () -> config.locations.dungeons.blazesolver, - newValue -> config.locations.dungeons.blazesolver = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveTrivia")) - .binding(defaults.locations.dungeons.solveTrivia, - () -> config.locations.dungeons.solveTrivia, - newValue -> config.locations.dungeons.solveTrivia = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveTicTacToe.@Tooltip"))) - .binding(defaults.locations.dungeons.solveTicTacToe, - () -> config.locations.dungeons.solveTicTacToe, - newValue -> config.locations.dungeons.solveTicTacToe = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - - //Livid Color - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.enableLividColor.@Tooltip"))) - .binding(defaults.locations.dungeons.lividColor.enableLividColor, - () -> config.locations.dungeons.lividColor.enableLividColor, - newValue -> config.locations.dungeons.lividColor.enableLividColor = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<String>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.lividColor.lividColorText.@Tooltip"))) - .binding(defaults.locations.dungeons.lividColor.lividColorText, - () -> config.locations.dungeons.lividColor.lividColorText, - newValue -> config.locations.dungeons.lividColor.lividColorText = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Terminal Solvers - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveColor")) - .binding(defaults.locations.dungeons.terminals.solveColor, - () -> config.locations.dungeons.terminals.solveColor, - newValue -> config.locations.dungeons.terminals.solveColor = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveOrder")) - .binding(defaults.locations.dungeons.terminals.solveOrder, - () -> config.locations.dungeons.terminals.solveOrder, - newValue -> config.locations.dungeons.terminals.solveOrder = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.terminals.solveStartsWith")) - .binding(defaults.locations.dungeons.terminals.solveStartsWith, - () -> config.locations.dungeons.terminals.solveStartsWith, - newValue -> config.locations.dungeons.terminals.solveStartsWith = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DwarvenMinesCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/DwarvenMinesCategory.java deleted file mode 100644 index 8330ba2a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/DwarvenMinesCategory.java +++ /dev/null @@ -1,94 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -import dev.isxander.yacl3.api.ButtonOption; -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 me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHudConfigScreen; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -public class DwarvenMinesCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines")) - - //Uncategorized Options - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.enableDrillFuel")) - .binding(defaults.locations.dwarvenMines.enableDrillFuel, - () -> config.locations.dwarvenMines.enableDrillFuel, - newValue -> config.locations.dwarvenMines.enableDrillFuel = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.solveFetchur")) - .binding(defaults.locations.dwarvenMines.solveFetchur, - () -> config.locations.dwarvenMines.solveFetchur, - newValue -> config.locations.dwarvenMines.solveFetchur = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.solvePuzzler")) - .binding(defaults.locations.dwarvenMines.solvePuzzler, - () -> config.locations.dwarvenMines.solvePuzzler, - newValue -> config.locations.dwarvenMines.solvePuzzler = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - - //Dwarven HUD - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud")) - .collapsed(false) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enabled")) - .binding(defaults.locations.dwarvenMines.dwarvenHud.enabled, - () -> config.locations.dwarvenMines.dwarvenHud.enabled, - newValue -> config.locations.dwarvenMines.dwarvenHud.enabled = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<SkyblockerConfig.DwarvenHudStyle>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[0]"), - Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[1]"), - Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.style.@Tooltip[2]"))) - .binding(defaults.locations.dwarvenMines.dwarvenHud.style, - () -> config.locations.dwarvenMines.dwarvenHud.style, - newValue -> config.locations.dwarvenMines.dwarvenHud.style = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(ButtonOption.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.screen")) - .text(Text.translatable("text.skyblocker.open")) - .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new DwarvenHudConfigScreen(screen))) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.enableBackground")) - .binding(defaults.locations.dwarvenMines.dwarvenHud.enableBackground, - () -> config.locations.dwarvenMines.dwarvenHud.enableBackground, - newValue -> config.locations.dwarvenMines.dwarvenHud.enableBackground = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.x")) - .binding(defaults.locations.dwarvenMines.dwarvenHud.x, - () -> config.locations.dwarvenMines.dwarvenHud.x, - newValue -> config.locations.dwarvenMines.dwarvenHud.x = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dwarvenMines.dwarvenHud.y")) - .binding(defaults.locations.dwarvenMines.dwarvenHud.y, - () -> config.locations.dwarvenMines.dwarvenHud.y, - newValue -> config.locations.dwarvenMines.dwarvenHud.y = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .build()) - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java deleted file mode 100644 index 318d579c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/GeneralCategory.java +++ /dev/null @@ -1,508 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -import dev.isxander.yacl3.api.*; -import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.FloatSliderControllerBuilder; -import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.skyblock.shortcut.ShortcutsConfigScreen; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainerConfigScreen; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -public class GeneralCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.category.general")) - - //Ungrouped Options - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.acceptReparty")) - .binding(defaults.general.acceptReparty, - () -> config.general.acceptReparty, - newValue -> config.general.acceptReparty = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.backpackPreviewWithoutShift")) - .binding(defaults.general.backpackPreviewWithoutShift, - () -> config.general.backpackPreviewWithoutShift, - newValue -> config.general.backpackPreviewWithoutShift = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.compactorDeletorPreview")) - .binding(defaults.general.compactorDeletorPreview, - () -> config.general.compactorDeletorPreview, - newValue -> config.general.compactorDeletorPreview = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.hideEmptyTooltips")) - .binding(defaults.general.hideEmptyTooltips, - () -> config.general.hideEmptyTooltips, - newValue -> config.general.hideEmptyTooltips = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.hideStatusEffectOverlay")) - .binding(defaults.general.hideStatusEffectOverlay, - () -> config.general.hideStatusEffectOverlay, - newValue -> config.general.hideStatusEffectOverlay = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - - //Tab Hud - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.tabHudEnabled")) - .binding(defaults.general.tabHud.tabHudEnabled, - () -> config.general.tabHud.tabHudEnabled, - newValue -> config.general.tabHud.tabHudEnabled = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.tabHudScale")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.tabHudScale.@Tooltip"))) - .binding(defaults.general.tabHud.tabHudScale, - () -> config.general.tabHud.tabHudScale, - newValue -> config.general.tabHud.tabHudScale = newValue) - .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(10, 200).step(1)) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.plainPlayerNames.@Tooltip"))) - .binding(defaults.general.tabHud.plainPlayerNames, - () -> config.general.tabHud.plainPlayerNames, - newValue -> config.general.tabHud.plainPlayerNames = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<SkyblockerConfig.NameSorting>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.nameSorting")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.tabHud.nameSorting.@Tooltip"))) - .binding(defaults.general.tabHud.nameSorting, - () -> config.general.tabHud.nameSorting, - newValue -> config.general.tabHud.nameSorting = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .build()) - - //Fancy Bars - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.enableBars")) - .binding(defaults.general.bars.enableBars, - () -> config.general.bars.enableBars, - newValue -> config.general.bars.enableBars = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<SkyblockerConfig.BarPosition>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.healthBarPosition")) - .binding(defaults.general.bars.barPositions.healthBarPosition, - () -> config.general.bars.barPositions.healthBarPosition, - newValue -> config.general.bars.barPositions.healthBarPosition = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<SkyblockerConfig.BarPosition>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.manaBarPosition")) - .binding(defaults.general.bars.barPositions.manaBarPosition, - () -> config.general.bars.barPositions.manaBarPosition, - newValue -> config.general.bars.barPositions.manaBarPosition = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<SkyblockerConfig.BarPosition>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.defenceBarPosition")) - .binding(defaults.general.bars.barPositions.defenceBarPosition, - () -> config.general.bars.barPositions.defenceBarPosition, - newValue -> config.general.bars.barPositions.defenceBarPosition = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<SkyblockerConfig.BarPosition>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.bars.barpositions.experienceBarPosition")) - .binding(defaults.general.bars.barPositions.experienceBarPosition, - () -> config.general.bars.barPositions.experienceBarPosition, - newValue -> config.general.bars.barPositions.experienceBarPosition = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .build()) - - //Experiments Solver - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments.enableChronomatronSolver")) - .binding(defaults.general.experiments.enableChronomatronSolver, - () -> config.general.experiments.enableChronomatronSolver, - newValue -> config.general.experiments.enableChronomatronSolver = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments.enableSuperpairsSolver")) - .binding(defaults.general.experiments.enableSuperpairsSolver, - () -> config.general.experiments.enableSuperpairsSolver, - newValue -> config.general.experiments.enableSuperpairsSolver = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.experiments.enableUltrasequencerSolver")) - .binding(defaults.general.experiments.enableUltrasequencerSolver, - () -> config.general.experiments.enableUltrasequencerSolver, - newValue -> config.general.experiments.enableUltrasequencerSolver = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Fishing Helper - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.fishing")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.fishing.enableFishingHelper")) - .binding(defaults.general.fishing.enableFishingHelper, - () -> config.general.fishing.enableFishingHelper, - newValue -> config.general.fishing.enableFishingHelper = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Fairy Souls Helper - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.enableFairySoulsHelper")) - .binding(defaults.general.fairySouls.enableFairySoulsHelper, - () -> config.general.fairySouls.enableFairySoulsHelper, - newValue -> config.general.fairySouls.enableFairySoulsHelper = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.highlightFoundSouls")) - .binding(defaults.general.fairySouls.highlightFoundSouls, - () -> config.general.fairySouls.highlightFoundSouls, - newValue -> config.general.fairySouls.highlightFoundSouls = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.fairySouls.highlightOnlyNearbySouls.@Tooltip"))) - .binding(defaults.general.fairySouls.highlightOnlyNearbySouls, - () -> config.general.fairySouls.highlightOnlyNearbySouls, - newValue -> config.general.fairySouls.highlightOnlyNearbySouls = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Item Cooldown - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemCooldown.enableItemCooldowns")) - .binding(defaults.general.itemCooldown.enableItemCooldowns, - () -> config.general.itemCooldown.enableItemCooldowns, - newValue -> config.general.itemCooldown.enableItemCooldowns = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Shortcuts - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableShortcuts.@Tooltip"))) - .binding(defaults.general.shortcuts.enableShortcuts, - () -> config.general.shortcuts.enableShortcuts, - newValue -> config.general.shortcuts.enableShortcuts = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandShortcuts.@Tooltip"))) - .binding(defaults.general.shortcuts.enableCommandShortcuts, - () -> config.general.shortcuts.enableCommandShortcuts, - newValue -> config.general.shortcuts.enableCommandShortcuts = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.enableCommandArgShortcuts.@Tooltip"))) - .binding(defaults.general.shortcuts.enableCommandArgShortcuts, - () -> config.general.shortcuts.enableCommandArgShortcuts, - newValue -> config.general.shortcuts.enableCommandArgShortcuts = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(ButtonOption.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.shortcuts.config")) - .text(Text.translatable("text.skyblocker.open")) - .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new ShortcutsConfigScreen(screen))) - .build()) - .build()) - - //Quiver Warning - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarning")) - .binding(defaults.general.quiverWarning.enableQuiverWarning, - () -> config.general.quiverWarning.enableQuiverWarning, - newValue -> config.general.quiverWarning.enableQuiverWarning = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningInDungeons")) - .binding(defaults.general.quiverWarning.enableQuiverWarningInDungeons, - () -> config.general.quiverWarning.enableQuiverWarningInDungeons, - newValue -> config.general.quiverWarning.enableQuiverWarningInDungeons = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.quiverWarning.enableQuiverWarningAfterDungeon")) - .binding(defaults.general.quiverWarning.enableQuiverWarningAfterDungeon, - () -> config.general.quiverWarning.enableQuiverWarningAfterDungeon, - newValue -> config.general.quiverWarning.enableQuiverWarningAfterDungeon = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Item List - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemList")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemList.enableItemList")) - .binding(defaults.general.itemList.enableItemList, - () -> config.general.itemList.enableItemList, - newValue -> config.general.itemList.enableItemList = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Item Tooltip - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableNPCPrice")) - .binding(defaults.general.itemTooltip.enableNPCPrice, - () -> config.general.itemTooltip.enableNPCPrice, - newValue -> config.general.itemTooltip.enableNPCPrice = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableMotesPrice.@Tooltip"))) - .binding(defaults.general.itemTooltip.enableMotesPrice, - () -> config.general.itemTooltip.enableMotesPrice, - newValue -> config.general.itemTooltip.enableMotesPrice = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableAvgBIN")) - .binding(defaults.general.itemTooltip.enableAvgBIN, - () -> config.general.itemTooltip.enableAvgBIN, - newValue -> config.general.itemTooltip.enableAvgBIN = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<SkyblockerConfig.Average>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.avg")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.avg.@Tooltip"))) - .binding(defaults.general.itemTooltip.avg, - () -> config.general.itemTooltip.avg, - newValue -> config.general.itemTooltip.avg = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableLowestBIN")) - .binding(defaults.general.itemTooltip.enableLowestBIN, - () -> config.general.itemTooltip.enableLowestBIN, - newValue -> config.general.itemTooltip.enableLowestBIN = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableBazaarPrice")) - .binding(defaults.general.itemTooltip.enableBazaarPrice, - () -> config.general.itemTooltip.enableBazaarPrice, - newValue -> config.general.itemTooltip.enableBazaarPrice = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemTooltip.enableMuseumDate")) - .binding(defaults.general.itemTooltip.enableMuseumDate, - () -> config.general.itemTooltip.enableMuseumDate, - newValue -> config.general.itemTooltip.enableMuseumDate = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Item Info Display - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo.@Tooltip"))) - .binding(defaults.general.itemInfoDisplay.attributeShardInfo, - () -> config.general.itemInfoDisplay.attributeShardInfo, - newValue -> config.general.itemInfoDisplay.attributeShardInfo = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgrounds.@Tooltip"))) - .binding(defaults.general.itemInfoDisplay.itemRarityBackgrounds, - () -> config.general.itemInfoDisplay.itemRarityBackgrounds, - newValue -> config.general.itemInfoDisplay.itemRarityBackgrounds = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Float>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.itemInfoDisplay.itemRarityBackgroundsOpacity")) - .binding(defaults.general.itemInfoDisplay.itemRarityBackgroundsOpacity, - () -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity, - newValue -> config.general.itemInfoDisplay.itemRarityBackgroundsOpacity = newValue) - .controller(opt -> FloatSliderControllerBuilder.create(opt).range(0f, 1f).step(0.05f).formatValue(ConfigUtils.FLOAT_TWO_FORMATTER)) - .build()) - .build()) - - //Special Effects - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.specialEffects.rareDungeonDropEffects.@Tooltip"))) - .binding(defaults.general.specialEffects.rareDungeonDropEffects, - () -> config.general.specialEffects.rareDungeonDropEffects, - newValue -> config.general.specialEffects.rareDungeonDropEffects = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Hitboxes - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox.oldFarmlandHitbox")) - .binding(defaults.general.hitbox.oldFarmlandHitbox, - () -> config.general.hitbox.oldFarmlandHitbox, - newValue -> config.general.hitbox.oldFarmlandHitbox = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.hitbox.oldLeverHitbox")) - .binding(defaults.general.hitbox.oldLeverHitbox, - () -> config.general.hitbox.oldLeverHitbox, - newValue -> config.general.hitbox.oldLeverHitbox = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //Title Container - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.@Tooltip"))) - .collapsed(true) - .option(Option.<Float>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.titleContainerScale")) - .binding(defaults.general.titleContainer.titleContainerScale, - () -> config.general.titleContainer.titleContainerScale, - newValue -> config.general.titleContainer.titleContainerScale = newValue) - .controller(opt -> FloatFieldControllerBuilder.create(opt).range(30f, 140f)) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.x")) - .binding(defaults.general.titleContainer.x, - () -> config.general.titleContainer.x, - newValue -> config.general.titleContainer.x = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.y")) - .binding(defaults.general.titleContainer.y, - () -> config.general.titleContainer.y, - newValue -> config.general.titleContainer.y = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<SkyblockerConfig.Direction>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.direction")) - .binding(defaults.general.titleContainer.direction, - () -> config.general.titleContainer.direction, - newValue -> config.general.titleContainer.direction = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<SkyblockerConfig.Alignment>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.alignment")) - .binding(defaults.general.titleContainer.alignment, - () -> config.general.titleContainer.alignment, - newValue -> config.general.titleContainer.alignment = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(ButtonOption.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.titleContainer.config")) - .text(Text.translatable("text.skyblocker.open")) - .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new TitleContainerConfigScreen(screen))) - .build()) - .build()) - - //Teleport Overlays - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableTeleportOverlays")) - .binding(defaults.general.teleportOverlay.enableTeleportOverlays, - () -> config.general.teleportOverlay.enableTeleportOverlays, - newValue -> config.general.teleportOverlay.enableTeleportOverlays = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableWeirdTransmission")) - .binding(defaults.general.teleportOverlay.enableWeirdTransmission, - () -> config.general.teleportOverlay.enableWeirdTransmission, - newValue -> config.general.teleportOverlay.enableWeirdTransmission = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableInstantTransmission")) - .binding(defaults.general.teleportOverlay.enableInstantTransmission, - () -> config.general.teleportOverlay.enableInstantTransmission, - newValue -> config.general.teleportOverlay.enableInstantTransmission = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableEtherTransmission")) - .binding(defaults.general.teleportOverlay.enableEtherTransmission, - () -> config.general.teleportOverlay.enableEtherTransmission, - newValue -> config.general.teleportOverlay.enableEtherTransmission = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableSinrecallTransmission")) - .binding(defaults.general.teleportOverlay.enableSinrecallTransmission, - () -> config.general.teleportOverlay.enableSinrecallTransmission, - newValue -> config.general.teleportOverlay.enableSinrecallTransmission = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.general.teleportOverlay.enableWitherImpact")) - .binding(defaults.general.teleportOverlay.enableWitherImpact, - () -> config.general.teleportOverlay.enableWitherImpact, - newValue -> config.general.teleportOverlay.enableWitherImpact = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/LocationsCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/LocationsCategory.java deleted file mode 100644 index 099c917c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/LocationsCategory.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -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.IntegerSliderControllerBuilder; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import net.minecraft.text.Text; - -public class LocationsCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.category.locations")) - - //Barn - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.barn")) - .collapsed(false) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.barn.solveHungryHiker")) - .binding(defaults.locations.barn.solveHungryHiker, - () -> config.locations.barn.solveHungryHiker, - newValue -> config.locations.barn.solveHungryHiker = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.barn.solveTreasureHunter")) - .binding(defaults.locations.barn.solveTreasureHunter, - () -> config.locations.barn.solveTreasureHunter, - newValue -> config.locations.barn.solveTreasureHunter = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - - //The Rift - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.rift")) - .collapsed(false) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.rift.mirrorverseWaypoints")) - .binding(defaults.locations.rift.mirrorverseWaypoints, - () -> config.locations.rift.mirrorverseWaypoints, - newValue -> config.locations.rift.mirrorverseWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.rift.mcGrubberStacks")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.rift.mcGrubberStacks.@Tooltip"))) - .binding(defaults.locations.rift.mcGrubberStacks, - () -> config.locations.rift.mcGrubberStacks, - newValue -> config.locations.rift.mcGrubberStacks = newValue) - .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(0, 5).step(1)) - .build()) - .build()) - - //Spider's Den - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.spidersDen")) - .collapsed(false) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.spidersDen.relics.enableRelicsHelper")) - .binding(defaults.locations.spidersDen.relics.enableRelicsHelper, - () -> config.locations.spidersDen.relics.enableRelicsHelper, - newValue -> config.locations.spidersDen.relics.enableRelicsHelper = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.locations.spidersDen.relics.highlightFoundRelics")) - .binding(defaults.locations.spidersDen.relics.highlightFoundRelics, - () -> config.locations.spidersDen.relics.highlightFoundRelics, - newValue -> config.locations.spidersDen.relics.highlightFoundRelics = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build()) - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/MessageFilterCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/MessageFilterCategory.java deleted file mode 100644 index 268dc9b9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/MessageFilterCategory.java +++ /dev/null @@ -1,98 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionDescription; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import net.minecraft.text.Text; - -public class MessageFilterCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.category.messages")) - - //Uncategorized Options - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAbility")) - .binding(defaults.messages.hideAbility, - () -> config.messages.hideAbility, - newValue -> config.messages.hideAbility = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideHeal")) - .binding(defaults.messages.hideHeal, - () -> config.messages.hideHeal, - newValue -> config.messages.hideHeal = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAOTE")) - .binding(defaults.messages.hideAOTE, - () -> config.messages.hideAOTE, - newValue -> config.messages.hideAOTE = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideImplosion")) - .binding(defaults.messages.hideImplosion, - () -> config.messages.hideImplosion, - newValue -> config.messages.hideImplosion = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideMoltenWave")) - .binding(defaults.messages.hideMoltenWave, - () -> config.messages.hideMoltenWave, - newValue -> config.messages.hideMoltenWave = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAds")) - .binding(defaults.messages.hideAds, - () -> config.messages.hideAds, - newValue -> config.messages.hideAds = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideTeleportPad")) - .binding(defaults.messages.hideTeleportPad, - () -> config.messages.hideTeleportPad, - newValue -> config.messages.hideTeleportPad = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideCombo")) - .binding(defaults.messages.hideCombo, - () -> config.messages.hideCombo, - newValue -> config.messages.hideCombo = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideAutopet")) - .binding(defaults.messages.hideAutopet, - () -> config.messages.hideAutopet, - newValue -> config.messages.hideAutopet = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<ChatFilterResult>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideShowOff")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.messages.hideShowOff.@Tooltip"))) - .binding(defaults.messages.hideShowOff, - () -> config.messages.hideShowOff, - newValue -> config.messages.hideShowOff = newValue) - .controller(ConfigUtils::createEnumCyclingListController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.messages.hideMana")) - .binding(defaults.messages.hideMana, - () -> config.messages.hideMana, - newValue -> config.messages.hideMana = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/QuickNavigationCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/QuickNavigationCategory.java deleted file mode 100644 index 3189a1f3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/QuickNavigationCategory.java +++ /dev/null @@ -1,605 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -import dev.isxander.yacl3.api.ConfigCategory; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.OptionGroup; -import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.StringControllerBuilder; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import net.minecraft.text.Text; - -public class QuickNavigationCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.category.quickNav")) - - //Toggle - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.enableQuickNav")) - .binding(defaults.quickNav.enableQuickNav, - () -> config.quickNav.enableQuickNav, - newValue -> config.quickNav.enableQuickNav = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - - //Button 1 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button1.item.itemName, - () -> config.quickNav.button1.item.itemName, - newValue -> config.quickNav.button1.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button2")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button2.item.itemName, - () -> config.quickNav.button2.item.itemName, - newValue -> config.quickNav.button2.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button3")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button3.item.itemName, - () -> config.quickNav.button3.item.itemName, - newValue -> config.quickNav.button3.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button4")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button4.item.itemName, - () -> config.quickNav.button4.item.itemName, - newValue -> config.quickNav.button4.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button5")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button5.item.itemName, - () -> config.quickNav.button5.item.itemName, - newValue -> config.quickNav.button5.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button6")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button6.item.itemName, - () -> config.quickNav.button6.item.itemName, - newValue -> config.quickNav.button6.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button6.clickEvent, - () -> config.quickNav.button6.clickEvent, - newValue -> config.quickNav.button6.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 7 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button7")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button7.item.itemName, - () -> config.quickNav.button7.item.itemName, - newValue -> config.quickNav.button7.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button7.clickEvent, - () -> config.quickNav.button7.clickEvent, - newValue -> config.quickNav.button7.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 8 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button8")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button8.item.itemName, - () -> config.quickNav.button8.item.itemName, - newValue -> config.quickNav.button8.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button8.clickEvent, - () -> config.quickNav.button8.clickEvent, - newValue -> config.quickNav.button8.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 9 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button9")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button9.item.itemName, - () -> config.quickNav.button9.item.itemName, - newValue -> config.quickNav.button9.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button9.clickEvent, - () -> config.quickNav.button9.clickEvent, - newValue -> config.quickNav.button9.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 10 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button10")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button10.item.itemName, - () -> config.quickNav.button10.item.itemName, - newValue -> config.quickNav.button10.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button10.clickEvent, - () -> config.quickNav.button10.clickEvent, - newValue -> config.quickNav.button10.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 11 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button11")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button11.item.itemName, - () -> config.quickNav.button11.item.itemName, - newValue -> config.quickNav.button11.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button11.clickEvent, - () -> config.quickNav.button11.clickEvent, - newValue -> config.quickNav.button11.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - //Button 12 - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button12")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.itemName")) - .binding(defaults.quickNav.button12.item.itemName, - () -> config.quickNav.button12.item.itemName, - newValue -> config.quickNav.button12.item.itemName = newValue) - .controller(StringControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.item.nbt")) - .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("text.autoconfig.skyblocker.option.quickNav.button1.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("text.autoconfig.skyblocker.option.quickNav.button1.clickEvent")) - .binding(defaults.quickNav.button12.clickEvent, - () -> config.quickNav.button12.clickEvent, - newValue -> config.quickNav.button12.clickEvent = newValue) - .controller(StringControllerBuilder::create) - .build()) - .build()) - - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/categories/SlayersCategory.java b/src/main/java/me/xmrvizzy/skyblocker/config/categories/SlayersCategory.java deleted file mode 100644 index febca720..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/categories/SlayersCategory.java +++ /dev/null @@ -1,116 +0,0 @@ -package me.xmrvizzy.skyblocker.config.categories; - -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.FloatFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.IntegerFieldControllerBuilder; -import dev.isxander.yacl3.api.controller.IntegerSliderControllerBuilder; -import me.xmrvizzy.skyblocker.config.ConfigUtils; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import net.minecraft.text.Text; - -public class SlayersCategory { - - public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig config) { - return ConfigCategory.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.category.slayer")) - - //Vampire Slayer - .group(OptionGroup.createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer")) - .collapsed(true) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableEffigyWaypoints")) - .binding(defaults.slayer.vampireSlayer.enableEffigyWaypoints, - () -> config.slayer.vampireSlayer.enableEffigyWaypoints, - newValue -> config.slayer.vampireSlayer.enableEffigyWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.compactEffigyWaypoints")) - .binding(defaults.slayer.vampireSlayer.compactEffigyWaypoints, - () -> config.slayer.vampireSlayer.compactEffigyWaypoints, - newValue -> config.slayer.vampireSlayer.compactEffigyWaypoints = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.effigyUpdateFrequency.@Tooltip"))) - .binding(defaults.slayer.vampireSlayer.effigyUpdateFrequency, - () -> config.slayer.vampireSlayer.effigyUpdateFrequency, - newValue -> config.slayer.vampireSlayer.effigyUpdateFrequency = newValue) - .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableHolyIceIndicator")) - .binding(defaults.slayer.vampireSlayer.enableHolyIceIndicator, - () -> config.slayer.vampireSlayer.enableHolyIceIndicator, - newValue -> config.slayer.vampireSlayer.enableHolyIceIndicator = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceIndicatorTickDelay")) - .binding(defaults.slayer.vampireSlayer.holyIceIndicatorTickDelay, - () -> config.slayer.vampireSlayer.holyIceIndicatorTickDelay, - newValue -> config.slayer.vampireSlayer.holyIceIndicatorTickDelay = newValue) - .controller(IntegerFieldControllerBuilder::create) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.holyIceUpdateFrequency.@Tooltip"))) - .binding(defaults.slayer.vampireSlayer.holyIceUpdateFrequency, - () -> config.slayer.vampireSlayer.holyIceUpdateFrequency, - newValue -> config.slayer.vampireSlayer.holyIceUpdateFrequency = newValue) - .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableHealingMelonIndicator")) - .binding(defaults.slayer.vampireSlayer.enableHealingMelonIndicator, - () -> config.slayer.vampireSlayer.enableHealingMelonIndicator, - newValue -> config.slayer.vampireSlayer.enableHealingMelonIndicator = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Float>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.healingMelonHealthThreshold")) - .binding(defaults.slayer.vampireSlayer.healingMelonHealthThreshold, - () -> config.slayer.vampireSlayer.healingMelonHealthThreshold, - newValue -> config.slayer.vampireSlayer.healingMelonHealthThreshold = newValue) - .controller(FloatFieldControllerBuilder::create) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableSteakStakeIndicator")) - .binding(defaults.slayer.vampireSlayer.enableSteakStakeIndicator, - () -> config.slayer.vampireSlayer.enableSteakStakeIndicator, - newValue -> config.slayer.vampireSlayer.enableSteakStakeIndicator = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.steakStakeUpdateFrequency.@Tooltip"))) - .binding(defaults.slayer.vampireSlayer.steakStakeUpdateFrequency, - () -> config.slayer.vampireSlayer.steakStakeUpdateFrequency, - newValue -> config.slayer.vampireSlayer.steakStakeUpdateFrequency = newValue) - .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) - .build()) - .option(Option.<Boolean>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.enableManiaIndicator")) - .binding(defaults.slayer.vampireSlayer.enableManiaIndicator, - () -> config.slayer.vampireSlayer.enableManiaIndicator, - newValue -> config.slayer.vampireSlayer.enableManiaIndicator = newValue) - .controller(ConfigUtils::createBooleanController) - .build()) - .option(Option.<Integer>createBuilder() - .name(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency")) - .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.slayer.vampireSlayer.maniaUpdateFrequency.@Tooltip"))) - .binding(defaults.slayer.vampireSlayer.maniaUpdateFrequency, - () -> config.slayer.vampireSlayer.maniaUpdateFrequency, - newValue -> config.slayer.vampireSlayer.maniaUpdateFrequency = newValue) - .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) - .build()) - .build()) - - .build(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java deleted file mode 100644 index 6db0028c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownController.java +++ /dev/null @@ -1,93 +0,0 @@ -package me.xmrvizzy.skyblocker.config.controllers; - -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.utils.Dimension; -import dev.isxander.yacl3.gui.AbstractWidget; -import dev.isxander.yacl3.gui.YACLScreen; -import dev.isxander.yacl3.gui.controllers.dropdown.AbstractDropdownController; -import org.jetbrains.annotations.NotNull; - -import java.util.Arrays; -import java.util.function.Function; -import java.util.stream.Stream; - -public class EnumDropdownController<E extends Enum<E>> extends AbstractDropdownController<E> { - /** - * The function used to convert enum constants to strings used for display, suggestion, and validation. Defaults to {@link Enum#toString}. - */ - protected final Function<E, String> toString; - - protected EnumDropdownController(Option<E> option, Function<E, String> toString) { - super(option); - this.toString = toString; - } - - @Override - public String getString() { - return toString.apply(option().pendingValue()); - } - - @Override - public void setFromString(String value) { - option().requestSet(getEnumFromString(value)); - } - - /** - * Searches through enum constants for one whose {@link #toString} result equals {@code value} - * - * @return The enum constant associated with the {@code value} or the pending value if none are found - * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy - */ - private E getEnumFromString(String value) { - value = value.toLowerCase(); - for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { - if (toString.apply(constant).toLowerCase().equals(value)) return constant; - } - - return option().pendingValue(); - } - - @Override - public boolean isValueValid(String value) { - value = value.toLowerCase(); - for (E constant : option().pendingValue().getDeclaringClass().getEnumConstants()) { - if (toString.apply(constant).equals(value)) return true; - } - - return false; - } - - @Override - protected String getValidValue(String value, int offset) { - return getValidEnumConstants(value) - .skip(offset) - .findFirst() - .orElseGet(this::getString); - } - - /** - * Filters and sorts through enum constants for those whose {@link #toString} result equals {@code value} - * - * @return a sorted stream containing enum constants associated with the {@code value} - * @implNote The return value of {@link #toString} on each enum constant should be unique in order to ensure accuracy - */ - @NotNull - protected Stream<String> getValidEnumConstants(String value) { - String valueLowerCase = value.toLowerCase(); - return Arrays.stream(option().pendingValue().getDeclaringClass().getEnumConstants()) - .map(this.toString) - .filter(constant -> constant.toLowerCase().contains(valueLowerCase)) - .sorted((s1, s2) -> { - String s1LowerCase = s1.toLowerCase(); - String s2LowerCase = s2.toLowerCase(); - if (s1LowerCase.startsWith(valueLowerCase) && !s2LowerCase.startsWith(valueLowerCase)) return -1; - if (!s1LowerCase.startsWith(valueLowerCase) && s2LowerCase.startsWith(valueLowerCase)) return 1; - return s1.compareTo(s2); - }); - } - - @Override - public AbstractWidget provideWidget(YACLScreen screen, Dimension<Integer> widgetDimension) { - return new EnumDropdownControllerElement<>(this, screen, widgetDimension); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java deleted file mode 100644 index a17f58d6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilder.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.config.controllers; - -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.api.controller.ControllerBuilder; - -import java.util.function.Function; - -public interface EnumDropdownControllerBuilder<E extends Enum<E>> extends ControllerBuilder<E> { - EnumDropdownControllerBuilder<E> toString(Function<E, String> toString); - - static <E extends Enum<E>> EnumDropdownControllerBuilder<E> create(Option<E> option) { - return new EnumDropdownControllerBuilderImpl<>(option); - } - - /** - * Creates a factory for {@link EnumDropdownControllerBuilder}s with the given function for converting enum constants to strings. - * Use this if a custom toString function for an enum is needed. - * Use it like this: - * <pre>{@code Option.<MyEnum>createBuilder().controller(createEnumDropdownControllerBuilder.getFactory(MY_CUSTOM_ENUM_TO_STRING_FUNCTION))}</pre> - * @param toString The function used to convert enum constants to strings used for display, suggestion, and validation - * @return a factory for {@link EnumDropdownControllerBuilder}s - * @param <E> the enum type - */ - static <E extends Enum<E>> Function<Option<E>, ControllerBuilder<E>> getFactory(Function<E, String> toString) { - return opt -> EnumDropdownControllerBuilder.create(opt).toString(toString); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java deleted file mode 100644 index 27878c86..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerBuilderImpl.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.config.controllers; - -import dev.isxander.yacl3.api.Controller; -import dev.isxander.yacl3.api.Option; -import dev.isxander.yacl3.impl.controller.AbstractControllerBuilderImpl; - -import java.util.function.Function; - -public class EnumDropdownControllerBuilderImpl<E extends Enum<E>> extends AbstractControllerBuilderImpl<E> implements EnumDropdownControllerBuilder<E> { - private Function<E, String> toString = Enum::toString; - - public EnumDropdownControllerBuilderImpl(Option<E> option) { - super(option); - } - - @Override - public EnumDropdownControllerBuilder<E> toString(Function<E, String> toString) { - this.toString = toString; - return this; - } - - @SuppressWarnings("UnstableApiUsage") - @Override - public Controller<E> build() { - return new EnumDropdownController<>(option, toString); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerElement.java b/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerElement.java deleted file mode 100644 index 6c5a0097..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/config/controllers/EnumDropdownControllerElement.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.xmrvizzy.skyblocker.config.controllers; - -import dev.isxander.yacl3.api.utils.Dimension; -import dev.isxander.yacl3.gui.YACLScreen; -import dev.isxander.yacl3.gui.controllers.dropdown.AbstractDropdownControllerElement; - -import java.util.List; - -public class EnumDropdownControllerElement<E extends Enum<E>> extends AbstractDropdownControllerElement<E, String> { - private final EnumDropdownController<E> controller; - - public EnumDropdownControllerElement(EnumDropdownController<E> control, YACLScreen screen, Dimension<Integer> dim) { - super(control, screen, dim); - this.controller = control; - } - - @Override - public List<String> computeMatchingValues() { - return controller.getValidEnumConstants(inputField).toList(); - } - - @Override - public String getString(String object) { - return object; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java b/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java deleted file mode 100644 index 76298612..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/events/ClientPlayerBlockBreakEvent.java +++ /dev/null @@ -1,23 +0,0 @@ -package me.xmrvizzy.skyblocker.events; - -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - -// Fabric API currently doesn't have an event for this -public class ClientPlayerBlockBreakEvent { - public static final Event<AfterBlockBreak> AFTER = EventFactory.createArrayBacked(AfterBlockBreak.class, - (listeners) -> (world, player, pos, state) -> { - for (AfterBlockBreak listener : listeners) { - listener.afterBlockBreak(world, player, pos, state); - } - }); - - @FunctionalInterface - public interface AfterBlockBreak { - void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java b/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java deleted file mode 100644 index 477d68b0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/events/SkyblockEvents.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.xmrvizzy.skyblocker.events; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; - -@Environment(EnvType.CLIENT) -public final class SkyblockEvents { - public static final Event<SkyblockEvents.SkyblockJoin> JOIN = EventFactory.createArrayBacked(SkyblockEvents.SkyblockJoin.class, callbacks -> () -> { - for (SkyblockEvents.SkyblockJoin callback : callbacks) { - callback.onSkyblockJoin(); - } - }); - - public static final Event<SkyblockEvents.SkyblockLeave> LEAVE = EventFactory.createArrayBacked(SkyblockEvents.SkyblockLeave.class, callbacks -> () -> { - for (SkyblockEvents.SkyblockLeave callback : callbacks) { - callback.onSkyblockLeave(); - } - }); - - @Environment(EnvType.CLIENT) - @FunctionalInterface - public interface SkyblockJoin { - void onSkyblockJoin(); - } - - @Environment(EnvType.CLIENT) - @FunctionalInterface - public interface SkyblockLeave { - void onSkyblockLeave(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java deleted file mode 100644 index df71396e..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/AbstractInventoryScreenMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen; - -@Mixin(AbstractInventoryScreen.class) -public class AbstractInventoryScreenMixin { - - @Inject(method = "drawStatusEffects", at = @At("HEAD"), cancellable = true) - private void skyblocker$dontDrawStatusEffects(CallbackInfo ci) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hideStatusEffectOverlay) ci.cancel(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ArmorTrimMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ArmorTrimMixin.java deleted file mode 100644 index 1076332f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ArmorTrimMixin.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import com.llamalad7.mixinextras.sugar.Local; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.item.ItemStack; -import net.minecraft.item.trim.ArmorTrim; -import net.minecraft.nbt.NbtCompound; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.Optional; - -@Mixin(ArmorTrim.class) -public class ArmorTrimMixin { - - @ModifyReturnValue(method = "getTrim", at = @At("RETURN")) - private static Optional<ArmorTrim> skyblocker$customArmorTrims(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional<ArmorTrim> original, @Local ItemStack stack) { - NbtCompound nbt = stack.getNbt(); - - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - Object2ObjectOpenHashMap<String, CustomArmorTrims.ArmorTrimId> customTrims = SkyblockerConfigManager.get().general.customArmorTrims; - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - if (customTrims.containsKey(itemUuid)) { - CustomArmorTrims.ArmorTrimId trimKey = customTrims.get(itemUuid); - return CustomArmorTrims.TRIMS_CACHE.getOrDefault(trimKey, original); - } - } - - return original; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java deleted file mode 100644 index 3eb13073..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/BatEntityMixin.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.mob.AmbientEntity; -import net.minecraft.entity.passive.BatEntity; -import net.minecraft.world.World; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(BatEntity.class) -public abstract class BatEntityMixin extends AmbientEntity { - protected BatEntityMixin(EntityType<? extends AmbientEntity> entityType, World world) { - super(entityType, world); - } - - @Override - public void onRemoved() { - super.onRemoved(); - DungeonSecrets.onBatRemoved(this); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java deleted file mode 100644 index 7efefafd..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java +++ /dev/null @@ -1,48 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.WrapWithCondition; -import com.llamalad7.mixinextras.sugar.Local; -import dev.cbyrne.betterinject.annotations.Inject; -import me.xmrvizzy.skyblocker.skyblock.FishingHelper; -import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; -import org.slf4j.Logger; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; - -@Mixin(ClientPlayNetworkHandler.class) -public abstract class ClientPlayNetworkHandlerMixin { - - @Inject(method = "onPlaySound", at = @At("RETURN")) - private void skyblocker$onPlaySound(PlaySoundS2CPacket packet) { - FishingHelper.onSound(packet); - } - - @SuppressWarnings("resource") - @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "STORE", ordinal = 0)) - private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity, @Local LivingEntity collector) { - DungeonSecrets.onItemPickup(itemEntity, collector, collector == MinecraftClient.getInstance().player); - return itemEntity; - } - - @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) - private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) { - return !Utils.isOnHypixel(); - } - - @WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelPlayerListWarning(Logger instance, String format, Object arg) { - return !Utils.isOnHypixel(); - } - - @WrapWithCondition(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { - return !Utils.isOnHypixel(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java deleted file mode 100644 index 8b2ec417..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerEntityMixin.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.mojang.authlib.GameProfile; - -import dev.cbyrne.betterinject.annotations.Inject; -import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; -import me.xmrvizzy.skyblocker.skyblock.item.ItemProtection; -import me.xmrvizzy.skyblocker.skyblock.rift.HealingMelonIndicator; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.network.AbstractClientPlayerEntity; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.world.ClientWorld; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -@Mixin(ClientPlayerEntity.class) -public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity { - public ClientPlayerEntityMixin(ClientWorld world, GameProfile profile) { - super(world, profile); - } - - @Inject(method = "dropSelectedItem", at = @At("HEAD"), cancellable = true) - public void skyblocker$dropSelectedItem(CallbackInfoReturnable<Boolean> cir) { - if (Utils.isOnSkyblock()) { - if (ItemProtection.isItemProtected(this.getInventory().getMainHandStack())) cir.setReturnValue(false); - HotbarSlotLock.handleDropSelectedItem(this.getInventory().selectedSlot, cir); - } - } - - @Inject(method = "updateHealth", at = @At("RETURN")) - public void skyblocker$updateHealth() { - HealingMelonIndicator.updateHealth(); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java deleted file mode 100644 index 3963f9d3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ClientPlayerInteractionManagerMixin.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; -import net.minecraft.block.BlockState; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerInteractionManager; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -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.CallbackInfoReturnable; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -@Mixin(ClientPlayerInteractionManager.class) -public class ClientPlayerInteractionManagerMixin { - @Shadow - @Final - private MinecraftClient client; - - @Inject(method = "breakBlock", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;onBroken(Lnet/minecraft/world/WorldAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V"), locals = LocalCapture.CAPTURE_FAILHARD) - private void skyblocker$onBlockBroken(BlockPos pos, CallbackInfoReturnable<Boolean> cir, World world, BlockState blockState) { - ClientPlayerBlockBreakEvent.AFTER.invoker().afterBlockBreak(world, this.client.player, pos, blockState); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java deleted file mode 100644 index 257a5127..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DrawContextMixin.java +++ /dev/null @@ -1,73 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.sugar.Local; -import com.llamalad7.mixinextras.sugar.ref.LocalRef; -import dev.cbyrne.betterinject.annotations.Arg; -import dev.cbyrne.betterinject.annotations.Inject; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.item.AttributeShards; -import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; -import me.xmrvizzy.skyblocker.utils.ItemUtils; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.Nullable; -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; - -@Mixin(DrawContext.class) -public abstract class DrawContextMixin { - @Shadow - @Final - private MatrixStack matrices; - - @Shadow - public abstract int drawText(TextRenderer textRenderer, @Nullable String text, int x, int y, int color, boolean shadow); - - @Inject(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", at = @At("HEAD")) - private void skyblocker$renderAttributeShardDisplay(@Arg TextRenderer textRenderer, @Arg ItemStack stack, @Arg(ordinal = 0) int x, @Arg(ordinal = 1) int y, @Local(argsOnly = true) LocalRef<String> countOverride) { - if (!SkyblockerConfigManager.get().general.itemInfoDisplay.attributeShardInfo) return; - - NbtCompound nbt = stack.getNbt(); - - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - - if (extraAttributes.getString("id").equals("ATTRIBUTE_SHARD")) { - NbtCompound attributesTag = extraAttributes.getCompound("attributes"); - String[] attributes = attributesTag.getKeys().toArray(String[]::new); - - if (attributes.length != 0) { - String attributeId = attributes[0]; - int attributeLevel = attributesTag.getInt(attributeId); - - //Set item count - countOverride.set(Integer.toString(attributeLevel)); - - //Draw the attribute name - this.matrices.push(); - this.matrices.translate(0f, 0f, 200f); - - String attributeInitials = AttributeShards.getShortName(attributeId); - - this.drawText(textRenderer, attributeInitials, x, y, Formatting.AQUA.getColorValue(), true); - - this.matrices.pop(); - } - } - } - } - - @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) - private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local ItemStack stack) { - return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/DyeableItemMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/DyeableItemMixin.java deleted file mode 100644 index bbe31472..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/DyeableItemMixin.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.item.DyeableItem; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(DyeableItem.class) -public interface DyeableItemMixin { - @ModifyReturnValue(method = "getColor", at = @At("RETURN")) - private int skyblocker$customDyeColor(int originalColor, ItemStack stack) { - NbtCompound nbt = stack.getNbt(); - - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - return SkyblockerConfigManager.get().general.customDyeColors.getOrDefault(itemUuid, originalColor); - } - - return originalColor; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java deleted file mode 100644 index 94053381..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/FarmlandBlockMixin.java +++ /dev/null @@ -1,38 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.block.Block; -import net.minecraft.block.BlockState; -import net.minecraft.block.FarmlandBlock; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.shape.VoxelShape; -import net.minecraft.util.shape.VoxelShapes; -import net.minecraft.world.BlockView; -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; - -@Mixin(FarmlandBlock.class) -public abstract class FarmlandBlockMixin extends Block { - @Shadow - @Final - protected static VoxelShape SHAPE; - - protected FarmlandBlockMixin(Settings settings) { - super(settings); - } - - @ModifyReturnValue(method = "getOutlineShape", at = @At("RETURN")) - private VoxelShape skyblocker$replaceOutlineShape(VoxelShape original) { - return Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hitbox.oldFarmlandHitbox ? VoxelShapes.fullCube() : original; - } - - @SuppressWarnings("deprecation") - @Override - public VoxelShape getCullingShape(BlockState state, BlockView world, BlockPos pos) { - return SHAPE; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenHandlerMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenHandlerMixin.java deleted file mode 100644 index be8d454d..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/GenericContainerScreenHandlerMixin.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.SkyblockerMod; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.GenericContainerScreenHandler; -import net.minecraft.screen.ScreenHandler; -import net.minecraft.screen.ScreenHandlerType; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; - -import java.util.List; - -@Mixin(GenericContainerScreenHandler.class) -public abstract class GenericContainerScreenHandlerMixin extends ScreenHandler { - protected GenericContainerScreenHandlerMixin(@Nullable ScreenHandlerType<?> type, int syncId) { - super(type, syncId); - } - - @Override - public void setStackInSlot(int slot, int revision, ItemStack stack) { - SkyblockerMod.getInstance().containerSolverManager.markDirty(); - super.setStackInSlot(slot, revision, stack); - } - - @Override - public void updateSlotStacks(int revision, List<ItemStack> stacks, ItemStack cursorStack) { - SkyblockerMod.getInstance().containerSolverManager.markDirty(); - super.updateSlotStacks(revision, stacks, cursorStack); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java deleted file mode 100644 index 7e94d660..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/HandledScreenMixin.java +++ /dev/null @@ -1,193 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.experiment.ChronomatronSolver; -import me.xmrvizzy.skyblocker.skyblock.experiment.ExperimentSolver; -import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver; -import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver; -import me.xmrvizzy.skyblocker.skyblock.item.BackpackPreview; -import me.xmrvizzy.skyblocker.skyblock.item.CompactorDeletorPreview; -import me.xmrvizzy.skyblocker.skyblock.item.ItemProtection; -import me.xmrvizzy.skyblocker.skyblock.item.ItemRarityBackgrounds; -import me.xmrvizzy.skyblocker.skyblock.item.WikiLookup; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.item.TooltipContext; -import net.minecraft.inventory.SimpleInventory; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.GenericContainerScreenHandler; -import net.minecraft.screen.ScreenHandler; -import net.minecraft.screen.slot.Slot; -import net.minecraft.screen.slot.SlotActionType; -import net.minecraft.text.Text; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyVariable; -import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.Map; -import java.util.regex.Matcher; - -@Mixin(HandledScreen.class) -public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen { - /** - * This is the slot id returned for when a click is outside of the screen's bounds - */ - @Unique - private static final int OUT_OF_BOUNDS_SLOT = -999; - - @Shadow - @Nullable - protected Slot focusedSlot; - - @Shadow - @Final - protected T handler; - - protected HandledScreenMixin(Text title) { - super(title); - } - - @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 && !this.client.options.inventoryKey.matchesKey(keyCode, scanCode) && WikiLookup.wikiLookup.matchesKey(keyCode, scanCode)) { - WikiLookup.openWiki(this.focusedSlot); - } - } - - @SuppressWarnings("DataFlowIssue") - // makes intellij be quiet about this.focusedSlot maybe being null. It's already null checked in mixined method. - @Inject(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V"), cancellable = true) - public void skyblocker$drawMouseOverTooltip(DrawContext context, int x, int y, CallbackInfo ci) { - if (!Utils.isOnSkyblock()) return; - - // Hide Empty Tooltips - if (SkyblockerConfigManager.get().general.hideEmptyTooltips && focusedSlot.getStack().getName().getString().equals(" ")) { - ci.cancel(); - } - - // Backpack Preview - boolean shiftDown = SkyblockerConfigManager.get().general.backpackPreviewWithoutShift ^ Screen.hasShiftDown(); - if (shiftDown && getTitle().getString().equals("Storage") && focusedSlot.inventory != client.player.getInventory() && BackpackPreview.renderPreview(context, focusedSlot.getIndex(), x, y)) { - ci.cancel(); - } - - // Compactor Preview - if (SkyblockerConfigManager.get().general.compactorDeletorPreview) { - ItemStack stack = focusedSlot.getStack(); - Matcher matcher = CompactorDeletorPreview.NAME.matcher(ItemRegistry.getInternalName(stack)); - if (matcher.matches() && CompactorDeletorPreview.drawPreview(context, stack, matcher.group("type"), matcher.group("size"), x, y)) { - ci.cancel(); - } - } - } - - @Redirect(method = "drawMouseoverTooltip", at = @At(value = "INVOKE", target = "Lnet/minecraft/screen/slot/Slot;getStack()Lnet/minecraft/item/ItemStack;", ordinal = 0)) - private ItemStack skyblocker$experimentSolvers$replaceTooltipDisplayStack(Slot slot) { - return skyblocker$experimentSolvers$getStack(slot, null); - } - - @ModifyVariable(method = "drawSlot", at = @At(value = "LOAD", ordinal = 4), ordinal = 0) - private ItemStack skyblocker$experimentSolvers$replaceDisplayStack(ItemStack stack, DrawContext context, Slot slot) { - return skyblocker$experimentSolvers$getStack(slot, stack); - } - - - @Unique - private ItemStack skyblocker$experimentSolvers$getStack(Slot slot, ItemStack stack) { - ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver(); - if ((currentSolver instanceof SuperpairsSolver || currentSolver instanceof UltrasequencerSolver) && ((ExperimentSolver) currentSolver).getState() == ExperimentSolver.State.SHOW && slot.inventory instanceof SimpleInventory) { - ItemStack itemStack = ((ExperimentSolver) currentSolver).getSlots().get(slot.getIndex()); - return itemStack == null ? slot.getStack() : itemStack; - } - return (stack != null) ? stack : slot.getStack(); - } - - @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerInteractionManager;clickSlot(IIILnet/minecraft/screen/slot/SlotActionType;Lnet/minecraft/entity/player/PlayerEntity;)V")) - private void skyblocker$experimentSolvers$onSlotClick(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { - if (slot != null) { - ContainerSolver currentSolver = SkyblockerMod.getInstance().containerSolverManager.getCurrentSolver(); - if (currentSolver instanceof ExperimentSolver experimentSolver && experimentSolver.getState() == ExperimentSolver.State.SHOW && slot.inventory instanceof SimpleInventory) { - if (experimentSolver instanceof ChronomatronSolver chronomatronSolver) { - Item item = chronomatronSolver.getChronomatronSlots().get(chronomatronSolver.getChronomatronCurrentOrdinal()); - if ((slot.getStack().isOf(item) || ChronomatronSolver.TERRACOTTA_TO_GLASS.get(slot.getStack().getItem()) == item) && chronomatronSolver.incrementChronomatronCurrentOrdinal() >= chronomatronSolver.getChronomatronSlots().size()) { - chronomatronSolver.setState(ExperimentSolver.State.END); - } - } else if (experimentSolver instanceof SuperpairsSolver superpairsSolver) { - superpairsSolver.setSuperpairsPrevClickedSlot(slot.getIndex()); - superpairsSolver.setSuperpairsCurrentSlot(ItemStack.EMPTY); - } else if (experimentSolver instanceof UltrasequencerSolver ultrasequencerSolver && slot.getIndex() == ultrasequencerSolver.getUltrasequencerNextSlot()) { - int count = ultrasequencerSolver.getSlots().get(ultrasequencerSolver.getUltrasequencerNextSlot()).getCount() + 1; - ultrasequencerSolver.getSlots().entrySet().stream().filter(entry -> entry.getValue().getCount() == count).findAny().map(Map.Entry::getKey).ifPresentOrElse(ultrasequencerSolver::setUltrasequencerNextSlot, () -> ultrasequencerSolver.setState(ExperimentSolver.State.END)); - } - } - } - } - - /** - * 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 - */ - @Inject(method = "onMouseClick(Lnet/minecraft/screen/slot/Slot;IILnet/minecraft/screen/slot/SlotActionType;)V", at = @At("HEAD"), cancellable = true) - private void skyblocker$onSlotInteract(Slot slot, int slotId, int button, SlotActionType actionType, CallbackInfo ci) { - if (Utils.isOnSkyblock()) { - // When you try and drop the item by picking it up then clicking outside of the screen - if (slotId == OUT_OF_BOUNDS_SLOT) { - ItemStack cursorStack = this.handler.getCursorStack(); - - if (ItemProtection.isItemProtected(cursorStack)) ci.cancel(); - } - - if (slot != null) { - // When you click your drop key while hovering over an item - if (actionType == SlotActionType.THROW) { - ItemStack stack = slot.getStack(); - - if (ItemProtection.isItemProtected(stack)) ci.cancel(); - } - - //Prevent salvaging - if (this.getTitle().getString().equals("Salvage Items")) { - ItemStack stack = slot.getStack(); - - if (ItemProtection.isItemProtected(stack)) ci.cancel(); - } - - //Prevent selling to NPC shops - if (this.client != null && this.handler instanceof GenericContainerScreenHandler genericContainerScreenHandler && genericContainerScreenHandler.getRows() == 6) { - ItemStack sellItem = this.handler.slots.get(49).getStack(); - - if (sellItem.getName().getString().equals("Sell Item") || skyblocker$doesLoreContain(sellItem, this.client, "buyback")) { - ItemStack stack = slot.getStack(); - - if (ItemProtection.isItemProtected(stack)) ci.cancel(); - } - } - } - } - } - - //TODO make this a util method somewhere else, eventually - private static boolean skyblocker$doesLoreContain(ItemStack stack, MinecraftClient client, String searchString) { - return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).anyMatch(line -> line.contains(searchString)); - } - - @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) - private void skyblocker$drawItemRarityBackground(DrawContext context, Slot slot, CallbackInfo ci) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(slot.getStack(), context, slot.x, slot.y); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java deleted file mode 100644 index 3376907b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InGameHudMixin.java +++ /dev/null @@ -1,93 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.sugar.Local; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.FancyStatusBars; -import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; -import me.xmrvizzy.skyblocker.skyblock.item.ItemCooldowns; -import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap; -import me.xmrvizzy.skyblocker.skyblock.item.ItemRarityBackgrounds; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.hud.InGameHud; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.Unique; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -@Environment(EnvType.CLIENT) -@Mixin(InGameHud.class) -public abstract class InGameHudMixin { - @Unique - private static final Identifier SLOT_LOCK = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/slot_lock.png"); - @Unique - private final FancyStatusBars statusBars = new FancyStatusBars(); - - @Shadow - private int scaledHeight; - @Shadow - private int scaledWidth; - - @Shadow - @Final - private MinecraftClient client; - - @Inject(method = "renderHotbar", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/hud/InGameHud;renderHotbarItem(Lnet/minecraft/client/gui/DrawContext;IIFLnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/item/ItemStack;I)V", ordinal = 0)) - public void skyblocker$renderHotbarItemLockOrRarityBg(float tickDelta, DrawContext context, CallbackInfo ci, @Local(ordinal = 4, name = "m") int index, @Local(ordinal = 5, name = "n") int x, @Local(ordinal = 6, name = "o") int y, @Local PlayerEntity player) { - if (Utils.isOnSkyblock()) { - if (SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) ItemRarityBackgrounds.tryDraw(player.getInventory().main.get(index), context, x, y); - if (HotbarSlotLock.isLocked(index)) context.drawTexture(SLOT_LOCK, x, y, 0, 0, 16, 16); - } - } - - @Inject(method = "renderExperienceBar", at = @At("HEAD"), cancellable = true) - private void skyblocker$renderExperienceBar(CallbackInfo ci) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.bars.enableBars && !Utils.isInTheRift()) - ci.cancel(); - } - - @Inject(method = "renderStatusBars", at = @At("HEAD"), cancellable = true) - private void skyblocker$renderStatusBars(DrawContext context, CallbackInfo ci) { - if (!Utils.isOnSkyblock()) - return; - if (statusBars.render(context, scaledWidth, scaledHeight)) - ci.cancel(); - - if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.enableMap) - DungeonMap.render(context.getMatrices()); - } - - @Inject(method = "renderMountHealth", at = @At("HEAD"), cancellable = true) - private void skyblocker$renderMountHealth(CallbackInfo ci) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.bars.enableBars && !Utils.isInTheRift()) - ci.cancel(); - } - - @Inject(method = "renderStatusEffectOverlay", at = @At("HEAD"), cancellable = true) - private void skyblocker$dontRenderStatusEffects(CallbackInfo ci) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.hideStatusEffectOverlay) ci.cancel(); - } - - @ModifyExpressionValue(method = "renderCrosshair", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayerEntity;getAttackCooldownProgress(F)F")) - private float skyblocker$modifyAttackIndicatorCooldown(float cooldownProgress) { - if (Utils.isOnSkyblock() && client.player != null) { - ItemStack stack = client.player.getMainHandStack(); - if (ItemCooldowns.isOnCooldown(stack)) { - return ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent(); - } - } - - return cooldownProgress; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java deleted file mode 100644 index 596fa1f9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/InventoryScreenMixin.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemListWidget; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.gui.screen.ingame.InventoryScreen; -import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(InventoryScreen.class) -public abstract class InventoryScreenMixin { - @ModifyExpressionValue(method = "<init>", at = @At(value = "NEW", target = "net/minecraft/client/gui/screen/recipebook/RecipeBookWidget")) - private RecipeBookWidget skyblocker$replaceRecipeBook(RecipeBookWidget original) { - return SkyblockerConfigManager.get().general.itemList.enableItemList && Utils.isOnSkyblock() ? new ItemListWidget() : original; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java deleted file mode 100644 index 99d42640..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import org.objectweb.asm.Opcodes; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; - -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; - -@Mixin(Item.class) -public abstract class ItemMixin { - @WrapOperation( - method = {"getItemBarColor", "getItemBarStep"}, - at = @At(value = "FIELD", target = "Lnet/minecraft/item/Item;maxDamage:I", opcode = Opcodes.GETFIELD) - ) - private int skyblocker$handlePickoDrillBar(Item item, Operation<Integer> original, ItemStack stack) { - return stack.getMaxDamage(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java deleted file mode 100644 index 280c01ba..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ItemStackMixin.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.ItemUtils; -import me.xmrvizzy.skyblocker.utils.ItemUtils.Durability; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; - -@Mixin(ItemStack.class) -public abstract class ItemStackMixin { - @Shadow - @Nullable - private NbtCompound nbt; - - @ModifyReturnValue(method = "getName", at = @At("RETURN")) - private Text skyblocker$customItemNames(Text original) { - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - return SkyblockerConfigManager.get().general.customItemNames.getOrDefault(itemUuid, original); - } - - return original; - } - - @ModifyReturnValue(method = "getDamage", at = @At("RETURN")) - private int skyblocker$handleDamage(int original) { - Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); - if (dur != null) { - return dur.max() - dur.current(); - } - return original; - } - - @ModifyReturnValue(method = "getMaxDamage", at = @At("RETURN")) - private int skyblocker$handleMaxDamage(int original) { - Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); - if (dur != null) { - return dur.max(); - } - return original; - } - - @ModifyReturnValue(method = "isDamageable", at = @At("RETURN")) - private boolean skyblocker$handleDamageable(boolean original) { - Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); - if (dur != null) { - return true; - } - return original; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java deleted file mode 100644 index d65b93a2..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/LeverBlockMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.skyblock.dungeon.OldLever; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.block.BlockState; -import net.minecraft.block.LeverBlock; -import net.minecraft.block.WallMountedBlock; -import net.minecraft.util.shape.VoxelShape; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import dev.cbyrne.betterinject.annotations.Arg; -import dev.cbyrne.betterinject.annotations.Inject; - -@Mixin(LeverBlock.class) -public abstract class LeverBlockMixin extends WallMountedBlock { - protected LeverBlockMixin(Settings settings) { - super(settings); - } - - @Inject(method = "getOutlineShape", at = @At("HEAD"), cancellable = true) - public void skyblocker$onGetOutlineShape(@Arg BlockState state, CallbackInfoReturnable<VoxelShape> cir) { - if (Utils.isOnSkyblock()) { - VoxelShape shape = OldLever.getShape(state.get(FACE), state.get(FACING)); - if (shape != null) cir.setReturnValue(shape); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java deleted file mode 100644 index 85f179e4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/MinecraftClientMixin.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.skyblock.HotbarSlotLock; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; -import org.spongepowered.asm.mixin.injection.At; -import dev.cbyrne.betterinject.annotations.Inject; - -@Mixin(MinecraftClient.class) -public abstract class MinecraftClientMixin { - @Shadow - @Nullable - public ClientPlayerEntity player; - - @Inject(method = "handleInputEvents", at = @At("HEAD")) - public void skyblocker$handleInputEvents() { - if (Utils.isOnSkyblock()) { - HotbarSlotLock.handleInputEvents(player); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java deleted file mode 100644 index 903e8ee0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerListHudMixin.java +++ /dev/null @@ -1,57 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud; -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenMaster; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.hud.PlayerListHud; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.text.Text; -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.callback.CallbackInfo; - -import dev.cbyrne.betterinject.annotations.Arg; -import dev.cbyrne.betterinject.annotations.Inject; - -@Environment(EnvType.CLIENT) -@Mixin(PlayerListHud.class) -public class PlayerListHudMixin { - @Shadow - private Text footer; - - @Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/client/gui/DrawContext;ILnet/minecraft/scoreboard/Scoreboard;Lnet/minecraft/scoreboard/ScoreboardObjective;)V", cancellable = true) - public void skyblocker$renderTabHud(@Arg DrawContext context, @Arg int w, CallbackInfo info) { - if (!Utils.isOnSkyblock() || !SkyblockerConfigManager.get().general.tabHud.tabHudEnabled || TabHud.defaultTgl.isPressed()) { - return; - } - - ClientPlayNetworkHandler nwH = MinecraftClient.getInstance().getNetworkHandler(); - if (nwH == null) { - return; - } - - int h = MinecraftClient.getInstance().getWindow().getScaledHeight(); - float scale = SkyblockerConfigManager.get().general.tabHud.tabHudScale / 100f; - w = (int) (w / scale); - h = (int) (h / scale); - - PlayerListMgr.updateFooter(footer); - - try { - ScreenMaster.render(context, w,h); - // Screen screen = Screen.getCorrect(w, h, footer); - // screen.render(context); - info.cancel(); - } catch (Exception e) { - TabHud.LOGGER.error("[Skyblocker] Encountered unknown exception while drawing default hud", e); - } - } - -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerSkinProviderMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerSkinProviderMixin.java deleted file mode 100644 index 4ca9a642..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/PlayerSkinProviderMixin.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import com.llamalad7.mixinextras.sugar.Local; -import com.mojang.authlib.GameProfile; -import com.mojang.authlib.minecraft.MinecraftSessionService; - -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.texture.PlayerSkinProvider.Textures; - -@Mixin(targets = "net.minecraft.client.texture.PlayerSkinProvider$1") -public class PlayerSkinProviderMixin { - - @ModifyReturnValue(method = "method_52867", at = @At("RETURN")) - private static Textures skyblocker$fixTexturesThatHadAnInvalidSignature(Textures texture, @Local MinecraftSessionService sessionService, @Local GameProfile profile) { - if (Utils.isOnHypixel() && texture == Textures.MISSING) { - try { - return Textures.fromMap(sessionService.getTextures(profile, false), false); - } catch (Throwable t) { - return Textures.MISSING; - } - } - - return texture; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/ScoreboardMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/ScoreboardMixin.java deleted file mode 100644 index 90edde6f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/ScoreboardMixin.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.WrapWithCondition; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.scoreboard.Scoreboard; -import org.slf4j.Logger; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -@Mixin(Scoreboard.class) -public abstract class ScoreboardMixin { - @WrapWithCondition(method = "addTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object arg) { - return !Utils.isOnHypixel(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java deleted file mode 100644 index 854c4e17..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/SocialInteractionsPlayerListWidgetMixin.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import java.util.Map; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; - -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.gui.screen.multiplayer.SocialInteractionsPlayerListEntry; -import net.minecraft.client.gui.screen.multiplayer.SocialInteractionsPlayerListWidget; - -@Mixin(SocialInteractionsPlayerListWidget.class) -public class SocialInteractionsPlayerListWidgetMixin { - - @WrapOperation(method = "setPlayers", at = @At(value = "INVOKE", target = "Ljava/util/Map;put(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", remap = false)) - private Object skyblocker$hideInvalidPlayers(Map<Object, Object> map, Object uuid, Object entry, Operation<Object> operation) { - if (Utils.isOnSkyblock() && !((SocialInteractionsPlayerListEntry) entry).getName().matches("[A-Za-z0-9_]+")) return null; - - return operation.call(map, uuid, entry); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java deleted file mode 100644 index 8cf94c69..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/WorldRendererMixin.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyVariable; - -import com.llamalad7.mixinextras.injector.ModifyExpressionValue; -import com.llamalad7.mixinextras.sugar.Local; -import com.llamalad7.mixinextras.sugar.Share; -import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.dungeon.StarredMobGlow; -import net.minecraft.client.render.WorldRenderer; -import net.minecraft.entity.Entity; - -@Mixin(WorldRenderer.class) -public class WorldRendererMixin { - - @ModifyExpressionValue(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/MinecraftClient;hasOutline(Lnet/minecraft/entity/Entity;)Z")) - private boolean skyblocker$shouldStarredMobGlow(boolean original, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) { - boolean isAStarredMobThatShouldGlow = SkyblockerConfigManager.get().locations.dungeons.starredMobGlow && StarredMobGlow.shouldMobGlow(entity); - - isGlowingStarredMob.set(isAStarredMobThatShouldGlow); - - return original || isAStarredMobThatShouldGlow; - } - - @ModifyVariable(method = "render", at = @At("STORE"), ordinal = 0) - private int skyblocker$modifyGlowColor(int color, @Local Entity entity, @Share("isGlowingStarredMob") LocalBooleanRef isGlowingStarredMob) { - return isGlowingStarredMob.get() ? StarredMobGlow.getGlowColor(entity) : color; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java deleted file mode 100644 index f0a0c768..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilMinecraftSessionServiceMixin.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import org.slf4j.Logger; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.mojang.authlib.yggdrasil.YggdrasilMinecraftSessionService; - -import me.xmrvizzy.skyblocker.utils.Utils; - -@Mixin(value = YggdrasilMinecraftSessionService.class, remap = false) -public class YggdrasilMinecraftSessionServiceMixin { - - @WrapOperation(method = "getSecurePropertyValue", remap = false, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private void skyblocker$dontLogMissingSignaturesOrTamperedProperties(Logger logger, String message, Object property, Operation<Void> operation) { - if (!Utils.isOnHypixel()) operation.call(logger, message, property); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java deleted file mode 100644 index 9abca5ad..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/YggdrasilServicesKeyInfoMixin.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.mojang.authlib.yggdrasil.YggdrasilServicesKeyInfo; - -import it.unimi.dsi.fastutil.ints.IntArrayList; -import it.unimi.dsi.fastutil.ints.IntList; -import me.xmrvizzy.skyblocker.utils.Utils; -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.Unique; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.Base64; -import java.util.Map; - -@Mixin(value = YggdrasilServicesKeyInfo.class, remap = false) -public class YggdrasilServicesKeyInfoMixin { - @Shadow - @Final - private static Logger LOGGER; - @Unique - private static final Map<String, String> REPLACEMENT_MAP = Map.of(); - @Unique - private static final IntList ERRONEUS_SIGNATURE_HASHES = new IntArrayList(); - - @WrapOperation(method = "validateProperty", at = @At(value = "INVOKE", target = "Ljava/util/Base64$Decoder;decode(Ljava/lang/String;)[B", remap = false), remap = false) - private byte[] skyblocker$replaceKnownWrongBase64(Base64.Decoder decoder, String signature, Operation<byte[]> decode) { - try { - return decode.call(decoder, signature); - } catch (IllegalArgumentException e) { - try { - return decode.call(decoder, signature.replaceAll("[^A-Za-z0-9+/=]", "")); - } catch (IllegalArgumentException e2) { - if (Utils.isOnSkyblock()) { - if (REPLACEMENT_MAP.containsKey(signature)) { - return decode.call(decoder, REPLACEMENT_MAP.get(signature)); - } - int signatureHashCode = signature.hashCode(); - if (!ERRONEUS_SIGNATURE_HASHES.contains(signatureHashCode)) { - ERRONEUS_SIGNATURE_HASHES.add(signatureHashCode); - LOGGER.warn("[Skyblocker Base64 Fixer] Failed to decode base64 string No.{}: {}", ERRONEUS_SIGNATURE_HASHES.size() - 1, signature); - } else { - LOGGER.warn("[Skyblocker Base64 Fixer] Failed to decode the base64 string No.{} again", ERRONEUS_SIGNATURE_HASHES.indexOf(signatureHashCode)); - } - } - } - throw e; - } - } - - @WrapOperation(method = "validateProperty", remap = false, at = @At(value = "INVOKE", target = "org/slf4j/Logger.error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) - private void skyblocker$dontLogFailedSignatureValidation(Logger logger, String message, Object property, Object exception, Operation<Void> operation) { - if (!Utils.isOnHypixel()) operation.call(logger, message, property, exception); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java deleted file mode 100644 index ff7c7cbc..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/BeaconBlockEntityRendererInvoker.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.block.entity.BeaconBlockEntityRenderer; -import net.minecraft.client.util.math.MatrixStack; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -@Mixin(BeaconBlockEntityRenderer.class) -public interface BeaconBlockEntityRendererInvoker { - @SuppressWarnings("unused") - @Invoker("renderBeam") - static void renderBeam(MatrixStack matrices, VertexConsumerProvider vertexConsumers, float tickDelta, long worldTime, int yOffset, int maxY, float[] color) { - throw new IllegalStateException("Mixin invoker failed to apply."); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java deleted file mode 100644 index f1e5b684..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/DrawContextInvoker.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.tooltip.TooltipComponent; -import net.minecraft.client.gui.tooltip.TooltipPositioner; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -import java.util.List; - -@Mixin(DrawContext.class) -public interface DrawContextInvoker { - - @Invoker - void invokeDrawTooltip(TextRenderer textRenderer, List<TooltipComponent> components, int x, int y, TooltipPositioner positioner); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java deleted file mode 100644 index 9dacbe34..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/FrustumInvoker.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.render.Frustum; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Invoker; - -/** - * Use {@link me.xmrvizzy.skyblocker.utils.render.FrustumUtils#isVisible(double, double, double, double, double, double) FrustumUtils#isVisible} which is shorter. For the purpose of avoiding object allocations! - */ -@Mixin(Frustum.class) -public interface FrustumInvoker { - @Invoker - boolean invokeIsVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/HandledScreenAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/HandledScreenAccessor.java deleted file mode 100644 index e6e0ebb7..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/HandledScreenAccessor.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(HandledScreen.class) -public interface HandledScreenAccessor { - @Accessor("x") - int getX(); - - @Accessor("y") - int getY(); - - @Accessor - int getBackgroundWidth(); - - @Accessor - int getBackgroundHeight(); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/PlayerListHudAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/PlayerListHudAccessor.java deleted file mode 100644 index 7e335d73..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/PlayerListHudAccessor.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.gui.hud.PlayerListHud; -import net.minecraft.client.network.PlayerListEntry; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Comparator; - -@Mixin(PlayerListHud.class) -public interface PlayerListHudAccessor { - - @Accessor("ENTRY_ORDERING") - static Comparator<PlayerListEntry> getOrdering() { - throw new AssertionError(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java deleted file mode 100644 index 0f20d4e4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/RecipeBookWidgetAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(RecipeBookWidget.class) -public interface RecipeBookWidgetAccessor { - @Accessor - String getSearchText(); - @Accessor - TextFieldWidget getSearchField(); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java deleted file mode 100644 index 6a671601..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/ScreenAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Mutable; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(Screen.class) -public interface ScreenAccessor { - @Accessor - @Mutable - void setTitle(Text title); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/WorldRendererAccessor.java b/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/WorldRendererAccessor.java deleted file mode 100644 index 831a3385..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/mixin/accessor/WorldRendererAccessor.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.xmrvizzy.skyblocker.mixin.accessor; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.WorldRenderer; - -@Mixin(WorldRenderer.class) -public interface WorldRendererAccessor { - @Accessor - Frustum getFrustum(); -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java deleted file mode 100644 index 46454fb3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FairySouls.java +++ /dev/null @@ -1,215 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import com.google.common.collect.ImmutableSet; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mojang.brigadier.CommandDispatcher; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.NEURepo; -import me.xmrvizzy.skyblocker.utils.PosUtils; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.text.Text; -import net.minecraft.util.DyeColor; -import net.minecraft.util.math.BlockPos; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; - -public class FairySouls { - private static final Logger LOGGER = LoggerFactory.getLogger(FairySouls.class); - private static CompletableFuture<Void> fairySoulsLoaded; - private static int maxSouls = 0; - private static final Map<String, Set<BlockPos>> fairySouls = new HashMap<>(); - private static final Map<String, Map<String, Set<BlockPos>>> foundFairies = new HashMap<>(); - - @SuppressWarnings("UnusedReturnValue") - public static CompletableFuture<Void> runAsyncAfterFairySoulsLoad(Runnable runnable) { - if (fairySoulsLoaded == null) { - LOGGER.error("Fairy Souls have not being initialized yet! Please ensure the Fairy Souls module is initialized before modules calling this method in SkyblockerMod#onInitializeClient. This error can be safely ignore in a test environment."); - return CompletableFuture.completedFuture(null); - } - return fairySoulsLoaded.thenRunAsync(runnable); - } - - public static int getFairySoulsSize(@Nullable String location) { - return location == null ? maxSouls : fairySouls.get(location).size(); - } - - public static void init() { - loadFairySouls(); - ClientLifecycleEvents.CLIENT_STOPPING.register(FairySouls::saveFoundFairySouls); - ClientCommandRegistrationCallback.EVENT.register(FairySouls::registerCommands); - WorldRenderEvents.AFTER_TRANSLUCENT.register(FairySouls::render); - ClientReceiveMessageEvents.GAME.register(FairySouls::onChatMessage); - } - - private static void loadFairySouls() { - fairySoulsLoaded = NEURepo.runAsyncAfterLoad(() -> { - try (BufferedReader reader = new BufferedReader(new FileReader(NEURepo.LOCAL_REPO_DIR.resolve("constants").resolve("fairy_souls.json").toFile()))) { - for (Map.Entry<String, JsonElement> fairySoulJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { - if (fairySoulJson.getKey().equals("//") || fairySoulJson.getKey().equals("Max Souls")) { - if (fairySoulJson.getKey().equals("Max Souls")) { - maxSouls = fairySoulJson.getValue().getAsInt(); - } - continue; - } - ImmutableSet.Builder<BlockPos> fairySoulsForLocation = ImmutableSet.builder(); - for (JsonElement fairySoul : fairySoulJson.getValue().getAsJsonArray().asList()) { - fairySoulsForLocation.add(PosUtils.parsePosString(fairySoul.getAsString())); - } - fairySouls.put(fairySoulJson.getKey(), fairySoulsForLocation.build()); - } - LOGGER.debug("[Skyblocker] Loaded fairy soul locations"); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to load fairy soul locations", e); - } - - try (BufferedReader reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()))) { - for (Map.Entry<String, JsonElement> foundFairiesForProfileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { - Map<String, Set<BlockPos>> foundFairiesForProfile = new HashMap<>(); - for (Map.Entry<String, JsonElement> foundFairiesForLocationJson : foundFairiesForProfileJson.getValue().getAsJsonObject().asMap().entrySet()) { - Set<BlockPos> foundFairiesForLocation = new HashSet<>(); - for (JsonElement foundFairy : foundFairiesForLocationJson.getValue().getAsJsonArray().asList()) { - foundFairiesForLocation.add(PosUtils.parsePosString(foundFairy.getAsString())); - } - foundFairiesForProfile.put(foundFairiesForLocationJson.getKey(), foundFairiesForLocation); - } - foundFairies.put(foundFairiesForProfileJson.getKey(), foundFairiesForProfile); - } - LOGGER.debug("[Skyblocker] Loaded found fairy souls"); - } catch (FileNotFoundException ignored) { - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to load found fairy souls", e); - } - }); - } - - private static void saveFoundFairySouls(MinecraftClient client) { - try (BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_fairy_souls.json").toFile()))) { - JsonObject foundFairiesJson = new JsonObject(); - for (Map.Entry<String, Map<String, Set<BlockPos>>> foundFairiesForProfile : foundFairies.entrySet()) { - JsonObject foundFairiesForProfileJson = new JsonObject(); - for (Map.Entry<String, Set<BlockPos>> foundFairiesForLocation : foundFairiesForProfile.getValue().entrySet()) { - JsonArray foundFairiesForLocationJson = new JsonArray(); - for (BlockPos foundFairy : foundFairiesForLocation.getValue()) { - foundFairiesForLocationJson.add(PosUtils.getPosString(foundFairy)); - } - foundFairiesForProfileJson.add(foundFairiesForLocation.getKey(), foundFairiesForLocationJson); - } - foundFairiesJson.add(foundFairiesForProfile.getKey(), foundFairiesForProfileJson); - } - SkyblockerMod.GSON.toJson(foundFairiesJson, writer); - writer.close(); - LOGGER.info("[Skyblocker] Saved found fairy souls"); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to write found fairy souls to file", e); - } - } - - private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - dispatcher.register(literal(SkyblockerMod.NAMESPACE) - .then(literal("fairySouls") - .then(literal("markAllInCurrentIslandFound").executes(context -> { - FairySouls.markAllFairiesOnCurrentIslandFound(); - context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllFound")); - return 1; - })) - .then(literal("markAllInCurrentIslandMissing").executes(context -> { - FairySouls.markAllFairiesOnCurrentIslandMissing(); - context.getSource().sendFeedback(Text.translatable("skyblocker.fairySouls.markAllMissing")); - return 1; - })))); - } - - private static void render(WorldRenderContext context) { - SkyblockerConfig.FairySouls fairySoulsConfig = SkyblockerConfigManager.get().general.fairySouls; - - if (fairySoulsConfig.enableFairySoulsHelper && fairySoulsLoaded.isDone() && fairySouls.containsKey(Utils.getLocationRaw())) { - for (BlockPos fairySoulPos : fairySouls.get(Utils.getLocationRaw())) { - boolean fairySoulNotFound = isFairySoulMissing(fairySoulPos); - if (!fairySoulsConfig.highlightFoundSouls && !fairySoulNotFound || fairySoulsConfig.highlightOnlyNearbySouls && fairySoulPos.getSquaredDistance(context.camera().getPos()) > 2500) { - continue; - } - float[] colorComponents = fairySoulNotFound ? DyeColor.GREEN.getColorComponents() : DyeColor.RED.getColorComponents(); - RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoulPos, colorComponents, 0.5F); - } - } - } - - private static void onChatMessage(Text text, boolean overlay) { - String message = text.getString(); - if (message.equals("You have already found that Fairy Soul!") || message.equals("§d§lSOUL! §fYou found a §dFairy Soul§f!")) { - markClosestFairyFound(); - } - } - - private static void markClosestFairyFound() { - if (!fairySoulsLoaded.isDone()) return; - PlayerEntity player = MinecraftClient.getInstance().player; - if (player == null) { - LOGGER.warn("[Skyblocker] Failed to mark closest fairy soul as found because player is null"); - return; - } - fairySouls.get(Utils.getLocationRaw()).stream() - .filter(FairySouls::isFairySoulMissing) - .min(Comparator.comparingDouble(fairySoulPos -> fairySoulPos.getSquaredDistance(player.getPos()))) - .filter(fairySoulPos -> fairySoulPos.getSquaredDistance(player.getPos()) <= 16) - .ifPresent(fairySoulPos -> { - initializeFoundFairiesForCurrentProfileAndLocation(); - foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).add(fairySoulPos); - }); - } - - private static boolean isFairySoulMissing(BlockPos fairySoulPos) { - Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile()); - if (foundFairiesForProfile == null) { - return true; - } - Set<BlockPos> foundFairiesForProfileAndLocation = foundFairiesForProfile.get(Utils.getLocationRaw()); - if (foundFairiesForProfileAndLocation == null) { - return true; - } - return !foundFairiesForProfileAndLocation.contains(fairySoulPos); - } - - public static void markAllFairiesOnCurrentIslandFound() { - initializeFoundFairiesForCurrentProfileAndLocation(); - foundFairies.get(Utils.getProfile()).get(Utils.getLocationRaw()).addAll(fairySouls.get(Utils.getLocationRaw())); - } - - public static void markAllFairiesOnCurrentIslandMissing() { - Map<String, Set<BlockPos>> foundFairiesForProfile = foundFairies.get(Utils.getProfile()); - if (foundFairiesForProfile != null) { - foundFairiesForProfile.remove(Utils.getLocationRaw()); - } - } - - private static void initializeFoundFairiesForCurrentProfileAndLocation() { - initializeFoundFairiesForProfileAndLocation(Utils.getProfile(), Utils.getLocationRaw()); - } - - private static void initializeFoundFairiesForProfileAndLocation(String profile, String location) { - foundFairies.computeIfAbsent(profile, profileKey -> new HashMap<>()); - foundFairies.get(profile).computeIfAbsent(location, locationKey -> new HashSet<>()); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java deleted file mode 100644 index a7428056..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FancyStatusBars.java +++ /dev/null @@ -1,192 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.util.Identifier; - -public class FancyStatusBars { - private static final Identifier BARS = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/bars.png"); - - private final MinecraftClient client = MinecraftClient.getInstance(); - private final StatusBarTracker statusBarTracker = SkyblockerMod.getInstance().statusBarTracker; - - private final StatusBar[] bars = new StatusBar[]{ - new StatusBar(0, 16733525, 2), // Health Bar - new StatusBar(1, 5636095, 2), // Intelligence Bar - new StatusBar(2, 12106180, 1), // Defence Bar - new StatusBar(3, 8453920, 1), // Experience Bar - }; - - // Positions to show the bars - // 0: Hotbar Layer 1, 1: Hotbar Layer 2, 2: Right of hotbar - // Anything outside the set values hides the bar - private final int[] anchorsX = new int[3]; - private final int[] anchorsY = new int[3]; - - public FancyStatusBars() { - moveBar(0, 0); - moveBar(1, 0); - moveBar(2, 0); - moveBar(3, 0); - } - - private int fill(int value, int max) { - return (100 * value) / max; - } - - public boolean render(DrawContext context, int scaledWidth, int scaledHeight) { - var player = client.player; - if (!SkyblockerConfigManager.get().general.bars.enableBars || player == null || Utils.isInTheRift()) - return false; - anchorsX[0] = scaledWidth / 2 - 91; - anchorsY[0] = scaledHeight - 33; - anchorsX[1] = anchorsX[0]; - anchorsY[1] = anchorsY[0] - 10; - anchorsX[2] = (scaledWidth / 2 + 91) + 2; - anchorsY[2] = scaledHeight - 16; - - bars[0].update(statusBarTracker.getHealth()); - bars[1].update(statusBarTracker.getMana()); - int def = statusBarTracker.getDefense(); - bars[2].fill[0] = fill(def, def + 100); - bars[2].text = def; - bars[3].fill[0] = (int) (100 * player.experienceProgress); - bars[3].text = player.experienceLevel; - - // Update positions of bars from config - for (int i = 0; i < 4; i++) { - int configAnchorNum = switch (i) { - case 0 -> SkyblockerConfigManager.get().general.bars.barPositions.healthBarPosition.toInt(); - case 1 -> SkyblockerConfigManager.get().general.bars.barPositions.manaBarPosition.toInt(); - case 2 -> SkyblockerConfigManager.get().general.bars.barPositions.defenceBarPosition.toInt(); - case 3 -> SkyblockerConfigManager.get().general.bars.barPositions.experienceBarPosition.toInt(); - default -> 0; - }; - - if (bars[i].anchorNum != configAnchorNum) - moveBar(i, configAnchorNum); - } - - for (var bar : bars) { - bar.draw(context); - } - for (var bar : bars) { - bar.drawText(context); - } - return true; - } - - public void moveBar(int bar, int location) { - // Set the bar to the new anchor - bars[bar].anchorNum = location; - - // Count how many bars are in each location - int layer1Count = 0, layer2Count = 0, rightCount = 0; - for (int i = 0; i < 4; i++) { - switch (bars[i].anchorNum) { - case 0 -> layer1Count++; - case 1 -> layer2Count++; - case 2 -> rightCount++; - } - } - - // Set the bars width and offsetX according to their anchor and how many bars are on that layer - int adjustedLayer1Count = 0, adjustedLayer2Count = 0, adjustedRightCount = 0; - for (int i = 0; i < 4; i++) { - switch (bars[i].anchorNum) { - case 0 -> { - bars[i].bar_width = (172 - ((layer1Count - 1) * 11)) / layer1Count; - bars[i].offsetX = adjustedLayer1Count * (bars[i].bar_width + 11 + (layer1Count == 3 ? 0 : 1)); - adjustedLayer1Count++; - } - case 1 -> { - bars[i].bar_width = (172 - ((layer2Count - 1) * 11)) / layer2Count; - bars[i].offsetX = adjustedLayer2Count * (bars[i].bar_width + 11 + (layer2Count == 3 ? 0 : 1)); - adjustedLayer2Count++; - } - case 2 -> { - bars[i].bar_width = 50; - bars[i].offsetX = adjustedRightCount * (50 + 11); - adjustedRightCount++; - } - } - } - } - - private class StatusBar { - public final int[] fill; - public int offsetX; - private final int v; - private final int text_color; - public int anchorNum; - public int bar_width; - public Object text; - - private StatusBar(int i, int textColor, int fillNum) { - this.v = i * 9; - this.text_color = textColor; - this.fill = new int[fillNum]; - this.fill[0] = 100; - this.anchorNum = 0; - this.text = ""; - } - - public void update(StatusBarTracker.Resource resource) { - int max = resource.max(); - int val = resource.value(); - this.fill[0] = fill(val, max); - this.fill[1] = fill(resource.overflow(), max); - this.text = val; - } - - public void draw(DrawContext context) { - // Dont draw if anchorNum is outside of range - if (anchorNum < 0 || anchorNum > 2) return; - - // Draw the icon for the bar - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX, anchorsY[anchorNum], 0, v, 9, 9); - - // Draw the background for the bar - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10, anchorsY[anchorNum], 10, v, 2, 9); - for (int i = 2; i < bar_width - 2; i += 58) { - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + i, anchorsY[anchorNum], 12, v, Math.min(58, bar_width - 2 - i), 9); - } - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 10 + bar_width - 2, anchorsY[anchorNum], 70, v, 2, 9); - - // Draw the filled part of the bar - for (int i = 0; i < fill.length; i++) { - int fill_width = this.fill[i] * (bar_width - 2) / 100; - if (fill_width >= 1) { - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11, anchorsY[anchorNum], 72 + i * 60, v, 1, 9); - } - for (int j = 1; j < fill_width - 1; j += 58) { - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + j, anchorsY[anchorNum], 73 + i * 60, v, Math.min(58, fill_width - 1 - j), 9); - } - if (fill_width == bar_width - 2) { - context.drawTexture(BARS, anchorsX[anchorNum] + offsetX + 11 + fill_width - 1, anchorsY[anchorNum], 131 + i * 60, v, 1, 9); - } - } - } - - public void drawText(DrawContext context) { - // Dont draw if anchorNum is outside of range - if (anchorNum < 0 || anchorNum > 2) return; - - TextRenderer textRenderer = client.textRenderer; - String text = this.text.toString(); - int x = anchorsX[anchorNum] + this.offsetX + 11 + (bar_width - textRenderer.getWidth(text)) / 2; - int y = anchorsY[anchorNum] - 3; - - final int[] offsets = new int[]{-1, 1}; - for (int i : offsets) { - context.drawText(textRenderer, text, x + i, y, 0, false); - context.drawText(textRenderer, text, x, y + i, 0, false); - } - context.drawText(textRenderer, text, x, y, text_color, false); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java deleted file mode 100644 index dca3f30b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/FishingHelper.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.render.title.Title; -import net.fabricmc.fabric.api.event.player.UseItemCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.FishingRodItem; -import net.minecraft.item.ItemStack; -import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; -import net.minecraft.util.Formatting; -import net.minecraft.util.TypedActionResult; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; - -public class FishingHelper { - private static final Title title = new Title("skyblocker.fishing.reelNow", Formatting.GREEN); - private static long startTime; - private static Vec3d normalYawVector; - - public static void init() { - UseItemCallback.EVENT.register((player, world, hand) -> { - ItemStack stack = player.getStackInHand(hand); - if (stack.getItem() instanceof FishingRodItem) { - if (player.fishHook == null) { - start(player); - } else { - reset(); - } - } - return TypedActionResult.pass(stack); - }); - } - - public static void start(PlayerEntity player) { - startTime = System.currentTimeMillis(); - float yawRad = player.getYaw() * 0.017453292F; - normalYawVector = new Vec3d(-MathHelper.sin(yawRad), 0, MathHelper.cos(yawRad)); - } - - public static void reset() { - startTime = 0; - } - - public static void onSound(PlaySoundS2CPacket packet) { - String path = packet.getSound().value().getId().getPath(); - if (SkyblockerConfigManager.get().general.fishing.enableFishingHelper && startTime != 0 && System.currentTimeMillis() >= startTime + 2000 && ("entity.generic.splash".equals(path) || "entity.player.splash".equals(path))) { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player != null && player.fishHook != null) { - Vec3d soundToFishHook = player.fishHook.getPos().subtract(packet.getX(), 0, packet.getZ()); - if (Math.abs(normalYawVector.x * soundToFishHook.z - normalYawVector.z * soundToFishHook.x) < 0.2D && Math.abs(normalYawVector.dotProduct(soundToFishHook)) < 4D && player.getPos().squaredDistanceTo(packet.getX(), packet.getY(), packet.getZ()) > 1D) { - RenderHelper.displayInTitleContainerAndPlaySound(title, 10); - reset(); - } - } else { - reset(); - } - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/HotbarSlotLock.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/HotbarSlotLock.java deleted file mode 100644 index 30dd1270..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/HotbarSlotLock.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.option.KeyBinding; -import org.lwjgl.glfw.GLFW; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; - -import java.util.List; - -public class HotbarSlotLock { - public static KeyBinding hotbarSlotLock; - - public static void init() { - hotbarSlotLock = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.hotbarSlotLock", - GLFW.GLFW_KEY_H, - "key.categories.skyblocker" - )); - } - - public static boolean isLocked(int slot) { - return SkyblockerConfigManager.get().general.lockedSlots.contains(slot); - } - - public static void handleDropSelectedItem(int slot, CallbackInfoReturnable<Boolean> cir) { - if (isLocked(slot)) cir.setReturnValue(false); - } - - public static void handleInputEvents(ClientPlayerEntity player) { - while (hotbarSlotLock.wasPressed()) { - List<Integer> lockedSlots = SkyblockerConfigManager.get().general.lockedSlots; - int selected = player.getInventory().selectedSlot; - if (!isLocked(player.getInventory().selectedSlot)) lockedSlots.add(selected); - else lockedSlots.remove(Integer.valueOf(selected)); - SkyblockerConfigManager.save(); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java deleted file mode 100644 index 2d0715e4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/QuiverWarning.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.hud.InGameHud; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.Nullable; - -public class QuiverWarning { - @Nullable - private static Type warning = null; - - public static void init() { - ClientReceiveMessageEvents.ALLOW_GAME.register(QuiverWarning::onChatMessage); - Scheduler.INSTANCE.scheduleCyclic(QuiverWarning::update, 10); - } - - public static boolean onChatMessage(Text text, boolean overlay) { - String message = text.getString(); - if (SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarning && message.endsWith("left in your Quiver!")) { - MinecraftClient.getInstance().inGameHud.setDefaultTitleFade(); - if (message.startsWith("You only have 50")) { - onChatMessage(Type.FIFTY_LEFT); - } else if (message.startsWith("You only have 10")) { - onChatMessage(Type.TEN_LEFT); - } else if (message.startsWith("You don't have any more")) { - onChatMessage(Type.EMPTY); - } - } - return true; - } - - private static void onChatMessage(Type warning) { - if (!Utils.isInDungeons()) { - MinecraftClient.getInstance().inGameHud.setTitle(Text.translatable(warning.key).formatted(Formatting.RED)); - } else if (SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarningInDungeons) { - MinecraftClient.getInstance().inGameHud.setTitle(Text.translatable(warning.key).formatted(Formatting.RED)); - QuiverWarning.warning = warning; - } - } - - public static void update() { - if (warning != null && SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarning && SkyblockerConfigManager.get().general.quiverWarning.enableQuiverWarningAfterDungeon && !Utils.isInDungeons()) { - InGameHud inGameHud = MinecraftClient.getInstance().inGameHud; - inGameHud.setDefaultTitleFade(); - inGameHud.setTitle(Text.translatable(warning.key).formatted(Formatting.RED)); - warning = null; - } - } - - private enum Type { - NONE(""), - FIFTY_LEFT("50Left"), - TEN_LEFT("10Left"), - EMPTY("empty"); - private final String key; - - Type(String key) { - this.key = "skyblocker.quiverWarning." + key; - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java deleted file mode 100644 index ae5aff0b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTracker.java +++ /dev/null @@ -1,109 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.text.Text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class StatusBarTracker { - private static final Pattern STATUS_HEALTH = Pattern.compile("§[6c](\\d+(,\\d\\d\\d)*)/(\\d+(,\\d\\d\\d)*)❤(?:(\\+§c(\\d+(,\\d\\d\\d)*). *)| *)"); - private static final Pattern DEFENSE_STATUS = Pattern.compile("§a(\\d+(,\\d\\d\\d)*)§a❈ Defense *"); - private static final Pattern MANA_USE = Pattern.compile("§b-(\\d+(,\\d\\d\\d)*) Mana \\(§\\S+(?:\\s\\S+)* *"); - private static final Pattern MANA_STATUS = Pattern.compile("§b(\\d+(,\\d\\d\\d)*)/(\\d+(,\\d\\d\\d)*)✎ (?:Mana|§3(\\d+(,\\d\\d\\d)*)ʬ) *"); - - private Resource health = new Resource(100, 100, 0); - private Resource mana = new Resource(100, 100, 0); - private int defense = 0; - - public void init() { - ClientReceiveMessageEvents.MODIFY_GAME.register(this::onOverlayMessage); - } - - public Resource getHealth() { - return this.health; - } - - public Resource getMana() { - return this.mana; - } - - public int getDefense() { - return this.defense; - } - - private int parseInt(Matcher m, int group) { - return Integer.parseInt(m.group(group).replace(",", "")); - } - - private void updateMana(Matcher m) { - int value = parseInt(m, 1); - int max = parseInt(m, 3); - int overflow = m.group(5) == null ? 0 : parseInt(m, 5); - this.mana = new Resource(value, max, overflow); - } - - private void updateHealth(Matcher m) { - int value = parseInt(m, 1); - int max = parseInt(m, 3); - int overflow = Math.max(0, value - max); - if (MinecraftClient.getInstance() != null && MinecraftClient.getInstance().player != null) { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - value = (int) (player.getHealth() * max / player.getMaxHealth()); - overflow = (int) (player.getAbsorptionAmount() * max / player.getMaxHealth()); - } - this.health = new Resource(Math.min(value, max), max, Math.min(overflow, max)); - } - - private String reset(String str, Matcher m) { - str = str.substring(m.end()); - m.reset(str); - return str; - } - - private Text onOverlayMessage(Text text, boolean overlay) { - if (!overlay || !Utils.isOnSkyblock() || !SkyblockerConfigManager.get().general.bars.enableBars || Utils.isInTheRift()) { - return text; - } - return Text.of(update(text.getString(), SkyblockerConfigManager.get().messages.hideMana)); - } - - public String update(String actionBar, boolean filterManaUse) { - var sb = new StringBuilder(); - Matcher matcher = STATUS_HEALTH.matcher(actionBar); - if (!matcher.lookingAt()) - return actionBar; - updateHealth(matcher); - if (matcher.group(5) != null) { - sb.append("§c❤"); - sb.append(matcher.group(5)); - } - actionBar = reset(actionBar, matcher); - if (matcher.usePattern(MANA_STATUS).lookingAt()) { - defense = 0; - updateMana(matcher); - actionBar = reset(actionBar, matcher); - } else { - if (matcher.usePattern(DEFENSE_STATUS).lookingAt()) { - defense = parseInt(matcher, 1); - actionBar = reset(actionBar, matcher); - } else if (filterManaUse && matcher.usePattern(MANA_USE).lookingAt()) { - actionBar = reset(actionBar, matcher); - } - if (matcher.usePattern(MANA_STATUS).find()) { - updateMana(matcher); - matcher.appendReplacement(sb, ""); - } - } - matcher.appendTail(sb); - String res = sb.toString().trim(); - return res.isEmpty() ? null : res; - } - - public record Resource(int value, int max, int overflow) { - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/TeleportOverlay.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/TeleportOverlay.java deleted file mode 100644 index 1aeeb6d3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/TeleportOverlay.java +++ /dev/null @@ -1,114 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import com.mojang.blaze3d.systems.RenderSystem; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.minecraft.block.BlockState; -import net.minecraft.client.MinecraftClient; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.hit.HitResult; -import net.minecraft.util.math.BlockPos; - -public class TeleportOverlay { - private static final float[] COLOR_COMPONENTS = {118f / 255f, 21f / 255f, 148f / 255f}; - private static final MinecraftClient client = MinecraftClient.getInstance(); - - public static void init() { - WorldRenderEvents.AFTER_TRANSLUCENT.register(TeleportOverlay::render); - } - - private static void render(WorldRenderContext wrc) { - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.teleportOverlay.enableTeleportOverlays && client.player != null && client.world != null) { - ItemStack heldItem = client.player.getMainHandStack(); - String itemId = PriceInfoTooltip.getInternalNameFromNBT(heldItem, true); - NbtCompound nbt = heldItem.getNbt(); - - if (itemId != null) { - switch (itemId) { - case "ASPECT_OF_THE_LEECH_1" -> { - if (SkyblockerConfigManager.get().general.teleportOverlay.enableWeirdTransmission) { - render(wrc, 3); - } - } - case "ASPECT_OF_THE_LEECH_2" -> { - if (SkyblockerConfigManager.get().general.teleportOverlay.enableWeirdTransmission) { - render(wrc, 4); - } - } - case "ASPECT_OF_THE_END", "ASPECT_OF_THE_VOID" -> { - if (SkyblockerConfigManager.get().general.teleportOverlay.enableEtherTransmission && client.options.sneakKey.isPressed() && nbt != null && nbt.getCompound("ExtraAttributes").getInt("ethermerge") == 1) { - render(wrc, nbt, 57); - } else if (SkyblockerConfigManager.get().general.teleportOverlay.enableInstantTransmission) { - render(wrc, nbt, 8); - } - } - case "ETHERWARP_CONDUIT" -> { - if (SkyblockerConfigManager.get().general.teleportOverlay.enableEtherTransmission) { - render(wrc, nbt, 57); - } - } - case "SINSEEKER_SCYTHE" -> { - if (SkyblockerConfigManager.get().general.teleportOverlay.enableSinrecallTransmission) { - render(wrc, nbt, 4); - } - } - case "NECRON_BLADE", "ASTRAEA", "HYPERION", "SCYLLA", "VALKYRIE" -> { - if (SkyblockerConfigManager.get().general.teleportOverlay.enableWitherImpact) { - render(wrc, 10); - } - } - } - } - } - } - - /** - * Renders the teleport overlay with a given base range and the tuned transmission stat. - */ - private static void render(WorldRenderContext wrc, NbtCompound nbt, int baseRange) { - render(wrc, nbt != null && nbt.getCompound("ExtraAttributes").contains("tuned_transmission") ? baseRange + nbt.getCompound("ExtraAttributes").getInt("tuned_transmission") : baseRange); - } - - /** - * Renders the teleport overlay with a given range. Uses {@link MinecraftClient#crosshairTarget} if it is a block and within range. Otherwise, raycasts from the player with the given range. - * - * @implNote {@link MinecraftClient#player} and {@link MinecraftClient#world} must not be null when calling this method. - */ - private static void render(WorldRenderContext wrc, int range) { - if (client.crosshairTarget != null && client.crosshairTarget.getType() == HitResult.Type.BLOCK && client.crosshairTarget instanceof BlockHitResult blockHitResult && client.crosshairTarget.squaredDistanceTo(client.player) < range * range) { - render(wrc, blockHitResult); - } else if (client.interactionManager != null && range > client.interactionManager.getReachDistance()) { - @SuppressWarnings("DataFlowIssue") - HitResult result = client.player.raycast(range, wrc.tickDelta(), false); - if (result.getType() == HitResult.Type.BLOCK && result instanceof BlockHitResult blockHitResult) { - render(wrc, blockHitResult); - } - } - } - - /** - * Renders the teleport overlay at the given {@link BlockHitResult}. - * - * @implNote {@link MinecraftClient#world} must not be null when calling this method. - */ - private static void render(WorldRenderContext wrc, BlockHitResult blockHitResult) { - BlockPos pos = blockHitResult.getBlockPos(); - @SuppressWarnings("DataFlowIssue") - BlockState state = client.world.getBlockState(pos); - if (!state.isAir() && client.world.getBlockState(pos.up()).isAir() && client.world.getBlockState(pos.up(2)).isAir()) { - RenderSystem.polygonOffset(-1f, -10f); - RenderSystem.enablePolygonOffset(); - - RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); - - RenderSystem.polygonOffset(0f, 0f); - RenderSystem.disablePolygonOffset(); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/HungryHiker.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/HungryHiker.java deleted file mode 100644 index 91862622..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/HungryHiker.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.barn; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; - -public class HungryHiker extends ChatPatternListener { - - private static final Map<String, String> foods; - - public HungryHiker() { super("^§e\\[NPC] Hungry Hiker§f: (The food I want is|(I asked for) food that is) ([a-zA-Z, '\\-]*\\.)$"); } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().locations.barn.solveHungryHiker ? ChatFilterResult.FILTER : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.player == null) return false; - String foodDescription = matcher.group(3); - String food = foods.get(foodDescription); - if (food == null) return false; - String middlePartOfTheMessageToSend = matcher.group(2) != null ? matcher.group(2) : matcher.group(1); - client.player.sendMessage(Text.of("§e[NPC] Hungry Hiker§f: " + middlePartOfTheMessageToSend + " " + food + "."), false); - return true; - } - - static { - foods = new HashMap<>(); - foods.put("from a cow.", Text.translatable("item.minecraft.cooked_beef").getString()); - foods.put("meat from a fowl.", Text.translatable("item.minecraft.cooked_chicken").getString()); - foods.put("red on the inside, green on the outside.", Text.translatable("item.minecraft.melon_slice").getString()); - foods.put("a cooked potato.", Text.translatable("item.minecraft.baked_potato").getString()); - foods.put("a stew.", Text.translatable("item.minecraft.rabbit_stew").getString()); - foods.put("a grilled meat.", Text.translatable("item.minecraft.cooked_porkchop").getString()); - foods.put("red and crunchy.", Text.translatable("item.minecraft.apple").getString()); - foods.put("made of wheat.", Text.translatable("item.minecraft.bread").getString()); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/TreasureHunter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/TreasureHunter.java deleted file mode 100644 index 385dc20b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/barn/TreasureHunter.java +++ /dev/null @@ -1,61 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.barn; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; - -public class TreasureHunter extends ChatPatternListener { - - private static final Map<String, String> locations; - - public TreasureHunter() { super("^§e\\[NPC] Treasure Hunter§f: ([a-zA-Z, '\\-\\.]*)$"); } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().locations.barn.solveTreasureHunter ? ChatFilterResult.FILTER : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.player == null) return false; - String hint = matcher.group(1); - String location = locations.get(hint); - if (location == null) return false; - client.player.sendMessage(Text.of("§e[NPC] Treasure Hunter§f: Go mine around " + location + "."), false); - return true; - } - - static { - locations = new HashMap<>(); - locations.put("There's a treasure chest somewhere in a small cave in the gorge.", "258 70 -492"); - locations.put("I was in the desert earlier, and I saw something near a red sand rock.", "357 82 -319"); - locations.put("There's this guy who collects animals to experiment on, I think I saw something near his house.", "259 184 -564"); - locations.put("There's a small house in the gorge, I saw some treasure near there.", "297 87 -562"); - locations.put("There's this guy who says he has the best sheep in the world. I think I saw something around his hut.", "392 85 -372"); - locations.put("I spotted something by an odd looking mushroom on one of the ledges in the Mushroom Gorge, you should check it out.", "305 73 -557"); - locations.put("There are some small ruins out in the desert, might want to check them out.", "320 102 -471"); - locations.put("Some dirt was kicked up by the water pool in the overgrown Mushroom Cave. Have a look over there.", "234 56 -410"); - locations.put("There are some old stone structures in the Mushroom Gorge, give them a look.", "223 54 -503"); - locations.put("In the Mushroom Gorge where blue meets the ceiling and floor, you will find what you are looking for.", "205 42 -527"); - locations.put("There was a haystack with a crop greener than usual around it, I think there is something near there.", "334 82 -389"); - locations.put("There's a single piece of tall grass growing in the desert, I saw something there.", "283 76 -363"); - locations.put("I saw some treasure by a cow skull near the village.", "141 77 -397"); - locations.put("Near a melon patch inside a tunnel in the mountain I spotted something.", "257 100 -569"); - locations.put("I saw something near a farmer's cart, you should check it out.", "155 90 -591"); - locations.put("I remember there was a stone pillar made only of cobblestone in the oasis, could be something there.", "122 66 -409"); - locations.put("I thought I saw something near the smallest stone pillar in the oasis.", "94 65 -455"); - locations.put("I found something by a mossy stone pillar in the oasis, you should take a look.", "179 93 -537"); - locations.put("Down in the glowing Mushroom Cave, there was a weird looking mushroom, check it out.", "182 44 -451"); - locations.put("Something caught my eye by the red sand near the bridge over the gorge.", "306 105 -489"); - locations.put("I seem to recall seeing something near the well in the village.", "170 77 -375"); - locations.put("I was down near the lower oasis yesterday, I think I saw something under the bridge.", "142 69 -448"); - locations.put("I was at the upper oasis today, I recall seeing something on the cobblestone stepping stones.", "188 77 -459"); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java deleted file mode 100644 index 67e6d7f4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java +++ /dev/null @@ -1,34 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; -import net.minecraft.item.ItemStack; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class CroesusHelper extends ContainerSolver { - - public CroesusHelper() { - super("^Croesus$"); - } - - @Override - protected boolean isEnabled() { - return SkyblockerConfigManager.get().locations.dungeons.croesusHelper; - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { - List<ColorHighlight> highlights = new ArrayList<>(); - for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) { - ItemStack stack = entry.getValue(); - if (stack != null && stack.getNbt() != null && (stack.getNbt().toString().contains("No more Chests to open!") || stack.getNbt().toString().contains("Opened Chest:"))) { - highlights.add(ColorHighlight.gray(entry.getKey())); - } - } - return highlights; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java deleted file mode 100644 index f39684eb..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java +++ /dev/null @@ -1,152 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import it.unimi.dsi.fastutil.objects.ObjectIntPair; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.predicate.entity.EntityPredicates; -import net.minecraft.util.math.Box; -import net.minecraft.util.math.Vec3d; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -/** - * This class provides functionality to render outlines around Blaze entities - */ -public class DungeonBlaze { - private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName()); - private static final float[] GREEN_COLOR_COMPONENTS = {0.0F, 1.0F, 0.0F}; - private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f}; - - private static ArmorStandEntity highestBlaze = null; - private static ArmorStandEntity lowestBlaze = null; - private static ArmorStandEntity nextHighestBlaze = null; - private static ArmorStandEntity nextLowestBlaze = null; - - public static void init() { - Scheduler.INSTANCE.scheduleCyclic(DungeonBlaze::update, 4); - WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer); - } - - /** - * Updates the state of Blaze entities and triggers the rendering process if necessary. - */ - public static void update() { - ClientWorld world = MinecraftClient.getInstance().world; - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (world == null || player == null || !Utils.isInDungeons()) return; - List<ObjectIntPair<ArmorStandEntity>> blazes = getBlazesInWorld(world, player); - sortBlazes(blazes); - updateBlazeEntities(blazes); - } - - /** - * Retrieves Blaze entities in the world and parses their health information. - * - * @param world The client world to search for Blaze entities. - * @return A list of Blaze entities and their associated health. - */ - private static List<ObjectIntPair<ArmorStandEntity>> getBlazesInWorld(ClientWorld world, ClientPlayerEntity player) { - List<ObjectIntPair<ArmorStandEntity>> blazes = new ArrayList<>(); - for (ArmorStandEntity blaze : world.getEntitiesByClass(ArmorStandEntity.class, player.getBoundingBox().expand(500D), EntityPredicates.NOT_MOUNTED)) { - String blazeName = blaze.getName().getString(); - if (blazeName.contains("Blaze") && blazeName.contains("/")) { - try { - int health = Integer.parseInt(blazeName.substring(blazeName.indexOf("/") + 1, blazeName.length() - 1)); - blazes.add(ObjectIntPair.of(blaze, health)); - } catch (NumberFormatException e) { - handleException(e); - } - } - } - return blazes; - } - - /** - * Sorts the Blaze entities based on their health values. - * - * @param blazes The list of Blaze entities to be sorted. - */ - private static void sortBlazes(List<ObjectIntPair<ArmorStandEntity>> blazes) { - blazes.sort(Comparator.comparingInt(ObjectIntPair::rightInt)); - } - - /** - * Updates information about Blaze entities based on sorted list. - * - * @param blazes The sorted list of Blaze entities with associated health values. - */ - private static void updateBlazeEntities(List<ObjectIntPair<ArmorStandEntity>> blazes) { - if (!blazes.isEmpty()) { - lowestBlaze = blazes.get(0).left(); - int highestIndex = blazes.size() - 1; - highestBlaze = blazes.get(highestIndex).left(); - if (blazes.size() > 1) { - nextLowestBlaze = blazes.get(1).left(); - nextHighestBlaze = blazes.get(highestIndex - 1).left(); - } - } - } - - /** - * Renders outlines for Blaze entities based on health and position. - * - * @param wrc The WorldRenderContext used for rendering. - */ - public static void blazeRenderer(WorldRenderContext wrc) { - try { - if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfigManager.get().locations.dungeons.blazesolver) { - if (highestBlaze.getY() < 69) { - renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc); - } - if (lowestBlaze.getY() > 69) { - renderBlazeOutline(lowestBlaze, nextLowestBlaze, wrc); - } - } - } catch (Exception e) { - handleException(e); - } - } - - /** - * Renders outlines for Blaze entities and connections between them. - * - * @param blaze The Blaze entity for which to render an outline. - * @param nextBlaze The next Blaze entity for connection rendering. - * @param wrc The WorldRenderContext used for rendering. - */ - private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) { - Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); - RenderHelper.renderOutline(wrc, blazeBox, GREEN_COLOR_COMPONENTS, 5f); - - if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) { - Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); - RenderHelper.renderOutline(wrc, nextBlazeBox, WHITE_COLOR_COMPONENTS, 5f); - - Vec3d blazeCenter = blazeBox.getCenter(); - Vec3d nextBlazeCenter = nextBlazeBox.getCenter(); - - RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f); - } - } - - /** - * Handles exceptions by logging and printing stack traces. - * - * @param e The exception to handle. - */ - private static void handleException(Exception e) { - LOGGER.warn("[Skyblocker BlazeRenderer] Encountered an unknown exception", e); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java deleted file mode 100644 index ea54ff32..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java +++ /dev/null @@ -1,169 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import com.google.gson.JsonObject; -import it.unimi.dsi.fastutil.ints.IntBooleanPair; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.mixin.accessor.ScreenAccessor; -import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.client.item.TooltipContext; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.GenericContainerScreenHandler; -import net.minecraft.screen.ScreenHandlerType; -import net.minecraft.screen.slot.Slot; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class DungeonChestProfit { - private static final Logger LOGGER = LoggerFactory.getLogger(DungeonChestProfit.class); - private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?<type>[A-Za-z]+) Essence x(?<amount>[0-9]+)"); - private static final DecimalFormat FORMATTER = new DecimalFormat("#,###"); - - public static void init() { - ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> ScreenEvents.afterTick(screen).register(screen1 -> { - if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getScreenHandler().getType() == ScreenHandlerType.GENERIC_9X6) { - ((ScreenAccessor) screen).setTitle(getChestProfit(genericContainerScreen.getScreenHandler(), screen.getTitle(), client)); - } - })); - } - - public static Text getChestProfit(GenericContainerScreenHandler handler, Text title, MinecraftClient client) { - try { - if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator && isDungeonChest(title.getString())) { - int profit = 0; - boolean hasIncompleteData = false, usedKismet = false; - List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9); - - //If the item stack for the "Open Reward Chest" button or the kismet button hasn't been sent to the client yet - if (slots.get(31).getStack().isEmpty() || slots.get(50).getStack().isEmpty()) return title; - - for (Slot slot : slots) { - ItemStack stack = slot.getStack(); - - if (!stack.isEmpty()) { - String name = stack.getName().getString(); - String id = PriceInfoTooltip.getInternalNameFromNBT(stack, false); - - //Regular item price - if (id != null) { - IntBooleanPair priceData = getItemPrice(id); - - if (!priceData.rightBoolean()) hasIncompleteData = true; - - //Add the item price to the profit - profit += priceData.leftInt(); - - continue; - } - - //Essence price - if (name.contains("Essence") && SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeEssence) { - Matcher matcher = ESSENCE_PATTERN.matcher(name); - - if (matcher.matches()) { - String type = matcher.group("type"); - int amount = Integer.parseInt(matcher.group("amount")); - - IntBooleanPair priceData = getItemPrice(("ESSENCE_" + type).toUpperCase()); - - if (!priceData.rightBoolean()) hasIncompleteData = true; - - //Add the price of the essence to the profit - profit += priceData.leftInt() * amount; - - continue; - } - } - - //Determine the cost of the chest - if (name.contains("Open Reward Chest")) { - String foundString = searchLoreFor(stack, client, "Coins"); - - //Incase we're searching the free chest - if (!StringUtils.isBlank(foundString)) { - profit -= Integer.parseInt(foundString.replaceAll("[^0-9]", "")); - } - - continue; - } - - //Determine if a kismet was used or not - if (name.contains("Reroll Chest")) { - usedKismet = !StringUtils.isBlank(searchLoreFor(stack, client, "You already rerolled a chest!")); - } - } - } - - if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeKismet && usedKismet) { - IntBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER"); - - if (!kismetPriceData.rightBoolean()) hasIncompleteData = true; - - profit -= kismetPriceData.leftInt(); - } - - return Text.literal(title.getString()).append(getProfitText(profit, hasIncompleteData)); - } - } catch (Exception e) { - LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e); - } - - return title; - } - - /** - * @return An {@link IntBooleanPair} with the {@code left int} representing the item's price, and the {@code right boolean} indicating if the price - * was based on complete data. - */ - private static IntBooleanPair getItemPrice(String id) { - JsonObject bazaarPrices = PriceInfoTooltip.getBazaarPrices(); - JsonObject lbinPrices = PriceInfoTooltip.getLBINPrices(); - - if (bazaarPrices == null || lbinPrices == null) return IntBooleanPair.of(0, false); - - if (bazaarPrices.has(id)) { - JsonObject item = bazaarPrices.get(id).getAsJsonObject(); - boolean isPriceNull = item.get("sellPrice").isJsonNull(); - - return IntBooleanPair.of(isPriceNull ? 0 : (int) item.get("sellPrice").getAsDouble(), !isPriceNull); - } - - if (lbinPrices.has(id)) { - return IntBooleanPair.of((int) lbinPrices.get(id).getAsDouble(), true); - } - - return IntBooleanPair.of(0, false); - } - - /** - * Searches for a specific string of characters in the name and lore of an item - */ - private static String searchLoreFor(ItemStack stack, MinecraftClient client, String searchString) { - return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).filter(line -> line.contains(searchString)).findAny().orElse(null); - } - - private static boolean isDungeonChest(String name) { - return name.equals("Wood Chest") || name.equals("Gold Chest") || name.equals("Diamond Chest") || name.equals("Emerald Chest") || name.equals("Obsidian Chest") || name.equals("Bedrock Chest"); - } - - private static Text getProfitText(int profit, boolean hasIncompleteData) { - SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit; - return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor); - } - - static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) { - return Text.literal((profit > 0 ? " +" : " ") + FORMATTER.format(profit)).formatted(hasIncompleteData ? incompleteColor : (Math.abs(profit) < neutralThreshold) ? neutralColor : (profit > 0) ? profitColor : lossColor); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java deleted file mode 100644 index 53cc4fac..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java +++ /dev/null @@ -1,61 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.render.MapRenderer; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.FilledMapItem; -import net.minecraft.item.ItemStack; -import net.minecraft.item.map.MapState; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.util.Identifier; -import org.apache.commons.lang3.StringUtils; - -public class DungeonMap { - private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png"); - - public static void render(MatrixStack matrices) { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.player == null || client.world == null) return; - ItemStack item = client.player.getInventory().main.get(8); - NbtCompound tag = item.getNbt(); - - if (tag != null && tag.contains("map")) { - String tag2 = tag.asString(); - tag2 = StringUtils.substringBetween(tag2, "map:", "}"); - int mapid = Integer.parseInt(tag2); - VertexConsumerProvider.Immediate vertices = client.getBufferBuilders().getEffectVertexConsumers(); - MapRenderer map = client.gameRenderer.getMapRenderer(); - MapState state = FilledMapItem.getMapState(mapid, client.world); - float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; - int x = SkyblockerConfigManager.get().locations.dungeons.mapX; - int y = SkyblockerConfigManager.get().locations.dungeons.mapY; - - if (state == null) return; - matrices.push(); - matrices.translate(x, y, 0); - matrices.scale(scaling, scaling, 0f); - map.draw(matrices, vertices, mapid, state, false, 15728880); - vertices.draw(); - matrices.pop(); - } - } - - public static void renderHUDMap(DrawContext context, int x, int y) { - float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; - int size = (int) (128 * scaling); - context.drawTexture(MAP_BACKGROUND, x, y, 0, 0, size, size, size, size); - } - - public static void init() { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("hud") - .then(ClientCommandManager.literal("dungeonmap") - .executes(Scheduler.queueOpenScreenCommand(DungeonMapConfigScreen::new)))))); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java deleted file mode 100644 index 94b05e86..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; - -import java.awt.*; - -public class DungeonMapConfigScreen extends Screen { - - private int hudX = SkyblockerConfigManager.get().locations.dungeons.mapX; - private int hudY = SkyblockerConfigManager.get().locations.dungeons.mapY; - private final Screen parent; - - protected DungeonMapConfigScreen() { - this(null); - } - - public DungeonMapConfigScreen(Screen parent) { - super(Text.literal("Dungeon Map Config")); - this.parent = parent; - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - renderBackground(context, mouseX, mouseY, delta); - DungeonMap.renderHUDMap(context, hudX, hudY); - context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width >> 1, height >> 1, Color.GRAY.getRGB()); - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling; - int size = (int) (128 * scaling); - if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + size, hudY + size) && button == 0) { - hudX = (int) Math.max(Math.min(mouseX - (size >> 1), this.width - size), 0); - hudY = (int) Math.max(Math.min(mouseY - (size >> 1), this.height - size), 0); - } - return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == 1) { - hudX = 2; - hudY = 2; - } - - return super.mouseClicked(mouseX, mouseY, button); - } - - @Override - public void close() { - SkyblockerConfigManager.get().locations.dungeons.mapX = hudX; - SkyblockerConfigManager.get().locations.dungeons.mapY = hudY; - SkyblockerConfigManager.save(); - this.client.setScreen(parent); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java deleted file mode 100644 index 2df8192d..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java +++ /dev/null @@ -1,42 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.util.math.BlockPos; - -public class LividColor { - private static int tenTicks = 0; - - public static void init() { - ClientReceiveMessageEvents.GAME.register((message, overlay) -> { - if (SkyblockerConfigManager.get().locations.dungeons.lividColor.enableLividColor && message.getString().equals("[BOSS] Livid: I respect you for making it to here, but I'll be your undoing.")) { - tenTicks = 8; - } - }); - } - - public static void update() { - MinecraftClient client = MinecraftClient.getInstance(); - if (tenTicks != 0) { - if (SkyblockerConfigManager.get().locations.dungeons.lividColor.enableLividColor && Utils.isInDungeons() && client.world != null) { - if (tenTicks == 1) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfigManager.get().locations.dungeons.lividColor.lividColorText.replace("[color]", "red")); - tenTicks = 0; - return; - } - String key = client.world.getBlockState(new BlockPos(5, 110, 42)).getBlock().getTranslationKey(); - if (key.startsWith("block.minecraft.") && key.endsWith("wool") && !key.endsWith("red_wool")) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfigManager.get().locations.dungeons.lividColor.lividColorText.replace("[color]", key.substring(16, key.length() - 5))); - tenTicks = 0; - return; - } - tenTicks--; - } else { - tenTicks = 0; - } - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java deleted file mode 100644 index b9fba7be..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import net.minecraft.block.Block; -import net.minecraft.block.enums.BlockFace; -import net.minecraft.util.math.Direction; -import net.minecraft.util.shape.VoxelShape; - -public class OldLever { - protected static final VoxelShape FLOOR_SHAPE = Block.createCuboidShape(4.0D, 0.0D, 4.0D, 12.0D, 10.0D, 12.0D); - protected static final VoxelShape NORTH_SHAPE = Block.createCuboidShape(5.0D, 3.0D, 10.0D, 11.0D, 13.0D, 16.0D); - protected static final VoxelShape SOUTH_SHAPE = Block.createCuboidShape(5.0D, 3.0D, 0.0D, 11.0D, 13.0D, 6.0D); - protected static final VoxelShape EAST_SHAPE = Block.createCuboidShape(0.0D, 3.0D, 5.0D, 6.0D, 13.0D, 11.0D); - protected static final VoxelShape WEST_SHAPE = Block.createCuboidShape(10.0D, 3.0D, 5.0D, 16.0D, 13.0D, 11.0D); - - public static VoxelShape getShape(BlockFace face, Direction direction) { - if (!SkyblockerConfigManager.get().general.hitbox.oldLeverHitbox) - return null; - - if (face == BlockFace.FLOOR) { - return FLOOR_SHAPE; - } else if (face == BlockFace.WALL) { - switch (direction) { - case EAST -> { - return EAST_SHAPE; - } - case WEST -> { - return WEST_SHAPE; - } - case SOUTH -> { - return SOUTH_SHAPE; - } - case NORTH -> { - return NORTH_SHAPE; - } - } - } - return null; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java deleted file mode 100644 index 288a8b5a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java +++ /dev/null @@ -1,94 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.text.Text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Reparty extends ChatPatternListener { - private static final MinecraftClient client = MinecraftClient.getInstance(); - public static final Pattern PLAYER = Pattern.compile(" ([a-zA-Z0-9_]{2,16}) ●"); - private static final int BASE_DELAY = 10; - - private String[] players; - private int playersSoFar; - private boolean repartying; - private String partyLeader; - - public Reparty() { - super("^(?:You are not currently in a party\\." + - "|Party (?:Membe|Moderato)rs(?: \\(([0-9]+)\\)|:( .*))" + - "|([\\[A-z+\\]]* )?(?<disband>.*) has disbanded .*" + - "|.*\n([\\[A-z+\\]]* )?(?<invite>.*) has invited you to join their party!" + - "\nYou have 60 seconds to accept. Click here to join!\n.*)$"); - - this.repartying = false; - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("rp").executes(context -> { - if (!Utils.isOnSkyblock() || this.repartying || client.player == null) return 0; - this.repartying = true; - MessageScheduler.INSTANCE.sendMessageAfterCooldown("/p list"); - return 0; - }))); - } - - @Override - public ChatFilterResult state() { - return (SkyblockerConfigManager.get().general.acceptReparty || this.repartying) ? ChatFilterResult.FILTER : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - if (matcher.group(1) != null && repartying) { - this.playersSoFar = 0; - this.players = new String[Integer.parseInt(matcher.group(1)) - 1]; - } else if (matcher.group(2) != null && repartying) { - Matcher m = PLAYER.matcher(matcher.group(2)); - while (m.find()) { - this.players[playersSoFar++] = m.group(1); - } - } else if (matcher.group("disband") != null && !matcher.group("disband").equals(client.getSession().getUsername())) { - partyLeader = matcher.group("disband"); - Scheduler.INSTANCE.schedule(() -> partyLeader = null, 61); - return false; - } else if (matcher.group("invite") != null && matcher.group("invite").equals(partyLeader)) { - String command = "/party accept " + partyLeader; - sendCommand(command, 0); - return false; - } else { - this.repartying = false; - return false; - } - if (this.playersSoFar == this.players.length) { - reparty(); - } - return false; - } - - private void reparty() { - ClientPlayerEntity playerEntity = client.player; - if (playerEntity == null) { - this.repartying = false; - return; - } - sendCommand("/p disband", 1); - for (int i = 0; i < this.players.length; ++i) { - String command = "/p invite " + this.players[i]; - sendCommand(command, i + 2); - } - Scheduler.INSTANCE.schedule(() -> this.repartying = false, this.players.length + 2); - } - - private void sendCommand(String command, int delay) { - MessageScheduler.INSTANCE.queueMessage(command, delay * BASE_DELAY); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java deleted file mode 100644 index f614662b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java +++ /dev/null @@ -1,56 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.culling.OcclusionCulling; -import net.minecraft.entity.Entity; -import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.entity.passive.BatEntity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.predicate.entity.EntityPredicates; -import net.minecraft.util.math.Box; - -import java.util.List; - -public class StarredMobGlow { - - public static boolean shouldMobGlow(Entity entity) { - Box box = entity.getBoundingBox(); - - if (Utils.isInDungeons() && !entity.isInvisible() && OcclusionCulling.isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) { - // Minibosses - if (entity instanceof PlayerEntity) { - switch (entity.getName().getString()) { - case "Lost Adventurer", "Shadow Assassin", "Diamond Guy" -> { - return true; - } - } - } - - // Regular Mobs - if (!(entity instanceof ArmorStandEntity)) { - Box searchBox = box.expand(0, 2, 0); - List<ArmorStandEntity> armorStands = entity.getWorld().getEntitiesByClass(ArmorStandEntity.class, searchBox, EntityPredicates.NOT_MOUNTED); - - if (!armorStands.isEmpty() && armorStands.get(0).getName().getString().contains("✯")) return true; - } - - // Bats - return entity instanceof BatEntity; - } - - return false; - } - - public static int getGlowColor(Entity entity) { - if (entity instanceof PlayerEntity) { - return switch (entity.getName().getString()) { - case "Lost Adventurer" -> 0xfee15c; - case "Shadow Assassin" -> 0x5b2cb2; - case "Diamond Guy" -> 0x57c2f7; - default -> 0xf57738; - }; - } - - return 0xf57738; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java deleted file mode 100644 index 05ed4d94..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.regex.Matcher; - -public class ThreeWeirdos extends ChatPatternListener { - public ThreeWeirdos() { - super("^§e\\[NPC] §c([A-Z][a-z]+)§f: (?:The reward is(?: not in my chest!|n't in any of our chests\\.)|My chest (?:doesn't have the reward\\. We are all telling the truth\\.|has the reward and I'm telling the truth!)|At least one of them is lying, and the reward is not in §c§c[A-Z][a-z]+'s §rchest\\!|Both of them are telling the truth\\. Also, §c§c[A-Z][a-z]+ §rhas the reward in their chest\\!)$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().locations.dungeons.solveThreeWeirdos ? null : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.player == null || client.world == null) return false; - client.world.getEntitiesByClass( - ArmorStandEntity.class, - client.player.getBoundingBox().expand(3), - entity -> { - Text customName = entity.getCustomName(); - return customName != null && customName.getString().equals(matcher.group(1)); - } - ).forEach( - entity -> entity.setCustomName(Text.of(Formatting.GREEN + matcher.group(1))) - ); - return false; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java deleted file mode 100644 index 21493da7..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java +++ /dev/null @@ -1,136 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.tictactoe.TicTacToeUtils; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.decoration.ItemFrameEntity; -import net.minecraft.item.FilledMapItem; -import net.minecraft.item.map.MapState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Box; -import net.minecraft.util.math.Direction; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -/** - * Thanks to Danker for a reference implementation! - */ -public class TicTacToe { - private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class); - private static final float[] RED_COLOR_COMPONENTS = {1.0F, 0.0F, 0.0F}; - private static Box nextBestMoveToMake = null; - - public static void init() { - WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer); - } - - public static void tick() { - MinecraftClient client = MinecraftClient.getInstance(); - ClientWorld world = client.world; - ClientPlayerEntity player = client.player; - - nextBestMoveToMake = null; - - if (world == null || player == null || !Utils.isInDungeons()) return; - - //Search within 21 blocks for item frames that contain maps - Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21); - List<ItemFrameEntity> itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap); - - try { - //Only attempt to solve if its the player's turn - if (itemFramesThatHoldMaps.size() != 9 && itemFramesThatHoldMaps.size() % 2 == 1) { - char[][] board = new char[3][3]; - BlockPos leftmostRow = null; - int sign = 1; - char facing = 'X'; - - for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) { - MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt())); - - if (mapState == null) continue; - - int column = 0, row; - sign = 1; - - //Find position of the item frame relative to where it is on the tic tac toe board - if (itemFrame.getHorizontalFacing() == Direction.SOUTH || itemFrame.getHorizontalFacing() == Direction.WEST) sign = -1; - BlockPos itemFramePos = BlockPos.ofFloored(itemFrame.getX(), itemFrame.getY(), itemFrame.getZ()); - - for (int i = 2; i >= 0; i--) { - int realI = i * sign; - BlockPos blockPos = itemFramePos; - - if (itemFrame.getX() % 0.5 == 0) { - blockPos = itemFramePos.add(realI, 0, 0); - } else if (itemFrame.getZ() % 0.5 == 0) { - blockPos = itemFramePos.add(0, 0, realI); - facing = 'Z'; - } - - Block block = world.getBlockState(blockPos).getBlock(); - if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) { - leftmostRow = blockPos; - column = i; - - break; - } - } - - //Determine the row of the item frame - if (itemFrame.getY() == 72.5) { - row = 0; - } else if (itemFrame.getY() == 71.5) { - row = 1; - } else if (itemFrame.getY() == 70.5) { - row = 2; - } else { - continue; - } - - - //Get the color of the middle pixel of the map which determines whether its X or O - int middleColor = mapState.colors[8256] & 255; - - if (middleColor == 114) { - board[row][column] = 'X'; - } else if (middleColor == 33) { - board[row][column] = 'O'; - } - - int bestMove = TicTacToeUtils.getBestMove(board) - 1; - - if (leftmostRow != null) { - double drawX = facing == 'X' ? leftmostRow.getX() - sign * (bestMove % 3) : leftmostRow.getX(); - double drawY = 72 - (double) (bestMove / 3); - double drawZ = facing == 'Z' ? leftmostRow.getZ() - sign * (bestMove % 3) : leftmostRow.getZ(); - - nextBestMoveToMake = new Box(drawX, drawY, drawZ, drawX + 1, drawY + 1, drawZ + 1); - } - } - } - } catch (Exception e) { - LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while determining a tic tac toe solution!", e); - } - } - - private static void solutionRenderer(WorldRenderContext context) { - try { - if (SkyblockerConfigManager.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) { - RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5); - } - } catch (Exception e) { - LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java deleted file mode 100644 index b1e51aa6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java +++ /dev/null @@ -1,100 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import me.xmrvizzy.skyblocker.skyblock.FairySouls; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.regex.Matcher; - -public class Trivia extends ChatPatternListener { - private static final Map<String, String[]> answers; - private List<String> solutions = Collections.emptyList(); - - public Trivia() { - super("^ +(?:([A-Za-z,' ]*\\?)|§6 ([ⓐⓑⓒ]) §a([a-zA-Z0-9 ]+))$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().locations.dungeons.solveTrivia ? ChatFilterResult.FILTER : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - String riddle = matcher.group(3); - if (riddle != null) { - if (!solutions.contains(riddle)) { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player != null) - MinecraftClient.getInstance().player.sendMessage(Text.of(" " + Formatting.GOLD + matcher.group(2) + Formatting.RED + " " + riddle), false); - return player != null; - } - } else updateSolutions(matcher.group(0)); - return false; - } - - private void updateSolutions(String question) { - String trimmedQuestion = question.trim(); - if (trimmedQuestion.equals("What SkyBlock year is it?")) { - long currentTime = System.currentTimeMillis() / 1000L; - long diff = currentTime - 1560276000; - int year = (int) (diff / 446400 + 1); - solutions = Collections.singletonList("Year " + year); - } else { - solutions = Arrays.asList(answers.get(trimmedQuestion)); - } - } - - static { - answers = Collections.synchronizedMap(new HashMap<>()); - answers.put("What is the status of The Watcher?", new String[]{"Stalker"}); - answers.put("What is the status of Bonzo?", new String[]{"New Necromancer"}); - answers.put("What is the status of Scarf?", new String[]{"Apprentice Necromancer"}); - answers.put("What is the status of The Professor?", new String[]{"Professor"}); - answers.put("What is the status of Thorn?", new String[]{"Shaman Necromancer"}); - answers.put("What is the status of Livid?", new String[]{"Master Necromancer"}); - answers.put("What is the status of Sadan?", new String[]{"Necromancer Lord"}); - answers.put("What is the status of Maxor?", new String[]{"The Wither Lords"}); - answers.put("What is the status of Goldor?", new String[]{"The Wither Lords"}); - answers.put("What is the status of Storm?", new String[]{"The Wither Lords"}); - answers.put("What is the status of Necron?", new String[]{"The Wither Lords"}); - answers.put("What is the status of Maxor, Storm, Goldor and Necron?", new String[]{"The Wither Lords"}); - answers.put("Which brother is on the Spider's Den?", new String[]{"Rick"}); - answers.put("What is the name of Rick's brother?", new String[]{"Pat"}); - answers.put("What is the name of the Painter in the Hub?", new String[]{"Marco"}); - answers.put("What is the name of the person that upgrades pets?", new String[]{"Kat"}); - answers.put("What is the name of the lady of the Nether?", new String[]{"Elle"}); - answers.put("Which villager in the Village gives you a Rogue Sword?", new String[]{"Jamie"}); - answers.put("How many unique minions are there?", new String[]{"59 Minions"}); - answers.put("Which of these enemies does not spawn in the Spider's Den?", new String[]{"Zombie Spider", "Cave Spider", "Wither Skeleton", "Dashing Spooder", "Broodfather", "Night Spider"}); - answers.put("Which of these monsters only spawns at night?", new String[]{"Zombie Villager", "Ghast"}); - answers.put("Which of these is not a dragon in The End?", new String[]{"Zoomer Dragon", "Weak Dragon", "Stonk Dragon", "Holy Dragon", "Boomer Dragon", "Booger Dragon", "Older Dragon", "Elder Dragon", "Stable Dragon", "Professor Dragon"}); - FairySouls.runAsyncAfterFairySoulsLoad(() -> { - answers.put("How many total Fairy Souls are there?", getFairySoulsSizeString(null)); - answers.put("How many Fairy Souls are there in Spider's Den?", getFairySoulsSizeString("combat_1")); - answers.put("How many Fairy Souls are there in The End?", getFairySoulsSizeString("combat_3")); - answers.put("How many Fairy Souls are there in The Farming Islands?", getFairySoulsSizeString("farming_1")); - answers.put("How many Fairy Souls are there in Crimson Isle?", getFairySoulsSizeString("crimson_isle")); - answers.put("How many Fairy Souls are there in The Park?", getFairySoulsSizeString("foraging_1")); - answers.put("How many Fairy Souls are there in Jerry's Workshop?", getFairySoulsSizeString("winter")); - answers.put("How many Fairy Souls are there in Hub?", getFairySoulsSizeString("hub")); - answers.put("How many Fairy Souls are there in The Hub?", getFairySoulsSizeString("hub")); - answers.put("How many Fairy Souls are there in Deep Caverns?", getFairySoulsSizeString("mining_2")); - answers.put("How many Fairy Souls are there in Gold Mine?", getFairySoulsSizeString("mining_1")); - answers.put("How many Fairy Souls are there in Dungeon Hub?", getFairySoulsSizeString("dungeon_hub")); - }); - } - - @NotNull - private static String[] getFairySoulsSizeString(@Nullable String location) { - return new String[]{"%d Fairy Souls".formatted(FairySouls.getFairySoulsSize(location))}; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java deleted file mode 100644 index 04cde662..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java +++ /dev/null @@ -1,275 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; - -import com.google.gson.JsonObject; -import it.unimi.dsi.fastutil.ints.IntSortedSet; -import it.unimi.dsi.fastutil.objects.ObjectIntPair; -import net.minecraft.block.MapColor; -import net.minecraft.item.map.MapIcon; -import net.minecraft.item.map.MapState; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3i; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joml.RoundingMode; -import org.joml.Vector2i; -import org.joml.Vector2ic; - -import java.util.*; - -public class DungeonMapUtils { - public static final byte BLACK_COLOR = MapColor.BLACK.getRenderColorByte(MapColor.Brightness.LOWEST); - public static final byte WHITE_COLOR = MapColor.WHITE.getRenderColorByte(MapColor.Brightness.HIGH); - - public static byte getColor(MapState map, @Nullable Vector2ic pos) { - return pos == null ? -1 : getColor(map, pos.x(), pos.y()); - } - - public static byte getColor(MapState map, int x, int z) { - if (x < 0 || z < 0 || x >= 128 || z >= 128) { - return -1; - } - return map.colors[x + (z << 7)]; - } - - public static boolean isEntranceColor(MapState map, int x, int z) { - return getColor(map, x, z) == Room.Type.ENTRANCE.color; - } - - public static boolean isEntranceColor(MapState map, @Nullable Vector2ic pos) { - return getColor(map, pos) == Room.Type.ENTRANCE.color; - } - - @Nullable - private static Vector2i getMapPlayerPos(MapState map) { - for (MapIcon icon : map.getIcons()) { - if (icon.type() == MapIcon.Type.FRAME) { - return new Vector2i((icon.x() >> 1) + 64, (icon.z() >> 1) + 64); - } - } - return null; - } - - @Nullable - public static ObjectIntPair<Vector2ic> getMapEntrancePosAndRoomSize(@NotNull MapState map) { - Vector2ic mapPos = getMapPlayerPos(map); - if (mapPos == null) { - return null; - } - Queue<Vector2ic> posToCheck = new ArrayDeque<>(); - Set<Vector2ic> checked = new HashSet<>(); - posToCheck.add(mapPos); - checked.add(mapPos); - while ((mapPos = posToCheck.poll()) != null) { - if (isEntranceColor(map, mapPos)) { - ObjectIntPair<Vector2ic> mapEntranceAndRoomSizePos = getMapEntrancePosAndRoomSizeAt(map, mapPos); - if (mapEntranceAndRoomSizePos.rightInt() > 0) { - return mapEntranceAndRoomSizePos; - } - } - Vector2ic pos = new Vector2i(mapPos).sub(10, 0); - if (checked.add(pos)) { - posToCheck.add(pos); - } - pos = new Vector2i(mapPos).sub(0, 10); - if (checked.add(pos)) { - posToCheck.add(pos); - } - pos = new Vector2i(mapPos).add(10, 0); - if (checked.add(pos)) { - posToCheck.add(pos); - } - pos = new Vector2i(mapPos).add(0, 10); - if (checked.add(pos)) { - posToCheck.add(pos); - } - } - return null; - } - - private static ObjectIntPair<Vector2ic> getMapEntrancePosAndRoomSizeAt(MapState map, Vector2ic mapPosImmutable) { - Vector2i mapPos = new Vector2i(mapPosImmutable); - // noinspection StatementWithEmptyBody - while (isEntranceColor(map, mapPos.sub(1, 0))) { - } - mapPos.add(1, 0); - //noinspection StatementWithEmptyBody - while (isEntranceColor(map, mapPos.sub(0, 1))) { - } - return ObjectIntPair.of(mapPos.add(0, 1), getMapRoomSize(map, mapPos)); - } - - public static int getMapRoomSize(MapState map, Vector2ic mapEntrancePos) { - int i = -1; - //noinspection StatementWithEmptyBody - while (isEntranceColor(map, mapEntrancePos.x() + ++i, mapEntrancePos.y())) { - } - return i > 5 ? i : 0; - } - - /** - * Gets the map position of the top left corner of the room the player is in. - * - * @param map the map - * @param mapEntrancePos the map position of the top left corner of the entrance - * @param mapRoomSize the size of a room on the map - * @return the map position of the top left corner of the room the player is in - * @implNote {@code mapPos} is shifted by 2 so room borders are evenly split. - * {@code mapPos} is then shifted by {@code offset} to align the top left most room at (0, 0) - * so subtracting the modulo will give the top left corner of the room shifted by {@code offset}. - * Finally, {@code mapPos} is shifted back by {@code offset} to its intended position. - */ - @Nullable - public static Vector2ic getMapRoomPos(MapState map, Vector2ic mapEntrancePos, int mapRoomSize) { - int mapRoomSizeWithGap = mapRoomSize + 4; - Vector2i mapPos = getMapPlayerPos(map); - if (mapPos == null) { - return null; - } - Vector2ic offset = new Vector2i(mapEntrancePos.x() % mapRoomSizeWithGap, mapEntrancePos.y() % mapRoomSizeWithGap); - return mapPos.add(2, 2).sub(offset).sub(mapPos.x() % mapRoomSizeWithGap, mapPos.y() % mapRoomSizeWithGap).add(offset); - } - - /** - * Gets the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room. - * - * @param physicalEntrancePos the physical position of the northwest corner of the entrance room - * @param mapEntrancePos the map position of the top left corner of the entrance room - * @param mapRoomSize the size of a room on the map - * @param physicalPos the physical position of the northwest corner of the room - * @return the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room - */ - public static Vector2ic getMapPosFromPhysical(Vector2ic physicalEntrancePos, Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalPos) { - return new Vector2i(physicalPos).sub(physicalEntrancePos).div(32).mul(mapRoomSize + 4).add(mapEntrancePos); - } - - /** - * @see #getPhysicalRoomPos(double, double) - */ - @NotNull - public static Vector2ic getPhysicalRoomPos(@NotNull Vec3d pos) { - return getPhysicalRoomPos(pos.getX(), pos.getZ()); - } - - /** - * @see #getPhysicalRoomPos(double, double) - */ - @NotNull - public static Vector2ic getPhysicalRoomPos(@NotNull Vec3i pos) { - return getPhysicalRoomPos(pos.getX(), pos.getZ()); - } - - /** - * Gets the physical position of the northwest corner of the room the given coordinate is in. Hypixel Skyblock Dungeons are aligned to a 32 by 32 blocks grid, allowing corners to be calculated through math. - * - * @param x the x position of the coordinate to calculate - * @param z the z position of the coordinate to calculate - * @return the physical position of the northwest corner of the room the player is in - * @implNote {@code physicalPos} is shifted by 0.5 so room borders are evenly split. - * {@code physicalPos} is further shifted by 8 because Hypixel offset dungeons by 8 blocks in Skyblock 0.12.3. - * Subtracting the modulo gives the northwest corner of the room shifted by 8. Finally, {@code physicalPos} is shifted back by 8 to its intended position. - */ - @NotNull - public static Vector2ic getPhysicalRoomPos(double x, double z) { - Vector2i physicalPos = new Vector2i(x + 8.5, z + 8.5, RoundingMode.TRUNCATE); - return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8); - } - - public static Vector2ic[] getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic... mapPositions) { - for (int i = 0; i < mapPositions.length; i++) { - mapPositions[i] = getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, mapPositions[i]); - } - return mapPositions; - } - - /** - * Gets the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room. - * - * @param mapEntrancePos the map position of the top left corner of the entrance room - * @param mapRoomSize the size of a room on the map - * @param physicalEntrancePos the physical position of the northwest corner of the entrance room - * @param mapPos the map position of the top left corner of the room - * @return the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room - */ - public static Vector2ic getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic mapPos) { - return new Vector2i(mapPos).sub(mapEntrancePos).div(mapRoomSize + 4).mul(32).add(physicalEntrancePos); - } - - public static Vector2ic getPhysicalCornerPos(Room.Direction direction, IntSortedSet segmentsX, IntSortedSet segmentsY) { - return switch (direction) { - case NW -> new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()); - case NE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.firstInt()); - case SW -> new Vector2i(segmentsX.firstInt(), segmentsY.lastInt() + 30); - case SE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.lastInt() + 30); - }; - } - - public static BlockPos actualToRelative(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) { - return switch (direction) { - case NW -> new BlockPos(pos.getX() - physicalCornerPos.x(), pos.getY(), pos.getZ() - physicalCornerPos.y()); - case NE -> new BlockPos(pos.getZ() - physicalCornerPos.y(), pos.getY(), -pos.getX() + physicalCornerPos.x()); - case SW -> new BlockPos(-pos.getZ() + physicalCornerPos.y(), pos.getY(), pos.getX() - physicalCornerPos.x()); - case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y()); - }; - } - - public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, JsonObject posJson) { - return relativeToActual(direction, physicalCornerPos, new BlockPos(posJson.get("x").getAsInt(), posJson.get("y").getAsInt(), posJson.get("z").getAsInt())); - } - - public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) { - return switch (direction) { - case NW -> new BlockPos(pos.getX() + physicalCornerPos.x(), pos.getY(), pos.getZ() + physicalCornerPos.y()); - case NE -> new BlockPos(-pos.getZ() + physicalCornerPos.x(), pos.getY(), pos.getX() + physicalCornerPos.y()); - case SW -> new BlockPos(pos.getZ() + physicalCornerPos.x(), pos.getY(), -pos.getX() + physicalCornerPos.y()); - case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y()); - }; - } - - public static Room.Type getRoomType(MapState map, Vector2ic mapPos) { - return switch (getColor(map, mapPos)) { - case 30 -> Room.Type.ENTRANCE; - case 63 -> Room.Type.ROOM; - case 66 -> Room.Type.PUZZLE; - case 62 -> Room.Type.TRAP; - case 74 -> Room.Type.MINIBOSS; - case 82 -> Room.Type.FAIRY; - case 18 -> Room.Type.BLOOD; - case 85 -> Room.Type.UNKNOWN; - default -> null; - }; - } - - public static Vector2ic[] getRoomSegments(MapState map, Vector2ic mapPos, int mapRoomSize, byte color) { - Set<Vector2ic> segments = new HashSet<>(); - Queue<Vector2ic> queue = new ArrayDeque<>(); - segments.add(mapPos); - queue.add(mapPos); - while (!queue.isEmpty()) { - Vector2ic curMapPos = queue.poll(); - Vector2i newMapPos = new Vector2i(); - if (getColor(map, newMapPos.set(curMapPos).sub(1, 0)) == color && !segments.contains(newMapPos.sub(mapRoomSize + 3, 0))) { - segments.add(newMapPos); - queue.add(newMapPos); - newMapPos = new Vector2i(); - } - if (getColor(map, newMapPos.set(curMapPos).sub(0, 1)) == color && !segments.contains(newMapPos.sub(0, mapRoomSize + 3))) { - segments.add(newMapPos); - queue.add(newMapPos); - newMapPos = new Vector2i(); - } - if (getColor(map, newMapPos.set(curMapPos).add(mapRoomSize, 0)) == color && !segments.contains(newMapPos.add(4, 0))) { - segments.add(newMapPos); - queue.add(newMapPos); - newMapPos = new Vector2i(); - } - if (getColor(map, newMapPos.set(curMapPos).add(0, mapRoomSize)) == color && !segments.contains(newMapPos.add(0, 4))) { - segments.add(newMapPos); - queue.add(newMapPos); - } - } - DungeonSecrets.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray())); - return segments.toArray(Vector2ic[]::new); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java deleted file mode 100644 index 4e4e9565..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ /dev/null @@ -1,451 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.arguments.IntegerArgumentType; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.builder.RequiredArgumentBuilder; -import it.unimi.dsi.fastutil.objects.Object2ByteMap; -import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; -import it.unimi.dsi.fastutil.objects.ObjectIntPair; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.fabricmc.fabric.api.event.player.UseBlockCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.mob.AmbientEntity; -import net.minecraft.entity.passive.BatEntity; -import net.minecraft.item.FilledMapItem; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.item.map.MapState; -import net.minecraft.resource.Resource; -import net.minecraft.text.Text; -import net.minecraft.util.ActionResult; -import net.minecraft.util.Identifier; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.joml.Vector2ic; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.zip.InflaterInputStream; - -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; - -public class DungeonSecrets { - protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class); - private static final String DUNGEONS_PATH = "dungeons"; - /** - * Maps the block identifier string to a custom numeric block id used in dungeon rooms data. - * - * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft. - */ - @SuppressWarnings("JavadocReference") - protected static final Object2ByteMap<String> NUMERIC_ID = new Object2ByteOpenHashMap<>(Map.ofEntries( - Map.entry("minecraft:stone", (byte) 1), - Map.entry("minecraft:diorite", (byte) 2), - Map.entry("minecraft:polished_diorite", (byte) 3), - Map.entry("minecraft:andesite", (byte) 4), - Map.entry("minecraft:polished_andesite", (byte) 5), - Map.entry("minecraft:grass_block", (byte) 6), - Map.entry("minecraft:dirt", (byte) 7), - Map.entry("minecraft:coarse_dirt", (byte) 8), - Map.entry("minecraft:cobblestone", (byte) 9), - Map.entry("minecraft:bedrock", (byte) 10), - Map.entry("minecraft:oak_leaves", (byte) 11), - Map.entry("minecraft:gray_wool", (byte) 12), - Map.entry("minecraft:double_stone_slab", (byte) 13), - Map.entry("minecraft:mossy_cobblestone", (byte) 14), - Map.entry("minecraft:clay", (byte) 15), - Map.entry("minecraft:stone_bricks", (byte) 16), - Map.entry("minecraft:mossy_stone_bricks", (byte) 17), - Map.entry("minecraft:chiseled_stone_bricks", (byte) 18), - Map.entry("minecraft:gray_terracotta", (byte) 19), - Map.entry("minecraft:cyan_terracotta", (byte) 20), - Map.entry("minecraft:black_terracotta", (byte) 21) - )); - /** - * Block data for dungeon rooms. See {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU} for format details and how it's generated. - * All access to this map must check {@link #isRoomsLoaded()} to prevent concurrent modification. - */ - @SuppressWarnings("JavadocReference") - protected static final HashMap<String, Map<String, Map<String, int[]>>> ROOMS_DATA = new HashMap<>(); - @NotNull - private static final Map<Vector2ic, Room> rooms = new HashMap<>(); - private static final Map<String, JsonElement> roomsJson = new HashMap<>(); - private static final Map<String, JsonElement> waypointsJson = new HashMap<>(); - @Nullable - private static CompletableFuture<Void> roomsLoaded; - /** - * The map position of the top left corner of the entrance room. - */ - @Nullable - private static Vector2ic mapEntrancePos; - /** - * The size of a room on the map. - */ - private static int mapRoomSize; - /** - * The physical position of the northwest corner of the entrance room. - */ - @Nullable - private static Vector2ic physicalEntrancePos; - private static Room currentRoom; - - public static boolean isRoomsLoaded() { - return roomsLoaded != null && roomsLoaded.isDone(); - } - - @SuppressWarnings("unused") - public static JsonObject getRoomMetadata(String room) { - return roomsJson.get(room).getAsJsonObject(); - } - - public static JsonArray getRoomWaypoints(String room) { - return waypointsJson.get(room).getAsJsonArray(); - } - - /** - * Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}. - * Use {@link #isRoomsLoaded()} to check for completion of loading. - */ - public static void init() { - if (SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.noInitSecretWaypoints) { - return; - } - // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set - CompletableFuture.runAsync(DungeonSecrets::load, MinecraftClient.getInstance()).exceptionally(e -> { - LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e); - return null; - }); - Scheduler.INSTANCE.scheduleCyclic(DungeonSecrets::update, 10); - WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render); - ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage); - ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage); - UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> onUseBlock(world, hitResult)); - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("secrets") - .then(literal("markAsFound").then(markSecretsCommand(true))) - .then(literal("markAsMissing").then(markSecretsCommand(false))))))); - ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset())); - } - - private static void load() { - long startTime = System.currentTimeMillis(); - List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); - for (Map.Entry<Identifier, Resource> resourceEntry : MinecraftClient.getInstance().getResourceManager().findResources(DUNGEONS_PATH, id -> id.getPath().endsWith(".skeleton")).entrySet()) { - String[] path = resourceEntry.getKey().getPath().split("/"); - if (path.length != 4) { - LOGGER.error("[Skyblocker] Failed to load dungeon secrets, invalid resource identifier {}", resourceEntry.getKey()); - break; - } - String dungeon = path[1]; - String roomShape = path[2]; - String room = path[3].substring(0, path[3].length() - ".skeleton".length()); - ROOMS_DATA.computeIfAbsent(dungeon, dungeonKey -> new HashMap<>()); - ROOMS_DATA.get(dungeon).computeIfAbsent(roomShape, roomShapeKey -> new HashMap<>()); - dungeonFutures.add(CompletableFuture.supplyAsync(() -> readRoom(resourceEntry.getValue())).thenAcceptAsync(rooms -> { - Map<String, int[]> roomsMap = ROOMS_DATA.get(dungeon).get(roomShape); - synchronized (roomsMap) { - roomsMap.put(room, rooms); - } - LOGGER.debug("[Skyblocker] Loaded dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room); - }).exceptionally(e -> { - LOGGER.error("[Skyblocker] Failed to load dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room, e); - return null; - })); - } - dungeonFutures.add(CompletableFuture.runAsync(() -> { - try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { - loadJson(roomsReader, roomsJson); - loadJson(waypointsReader, waypointsJson); - LOGGER.debug("[Skyblocker] Loaded dungeon secrets json"); - } catch (Exception e) { - LOGGER.error("[Skyblocker] Failed to load dungeon secrets json", e); - } - })); - roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker] Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total in {} ms", ROOMS_DATA.size(), ROOMS_DATA.values().stream().mapToInt(Map::size).sum(), ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).mapToInt(Map::size).sum(), System.currentTimeMillis() - startTime)).exceptionally(e -> { - LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e); - return null; - }); - LOGGER.info("[Skyblocker] Started loading dungeon secrets in (blocked main thread for) {} ms", System.currentTimeMillis() - startTime); - } - - private static int[] readRoom(Resource resource) throws RuntimeException { - try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(resource.getInputStream()))) { - return (int[]) in.readObject(); - } catch (IOException | ClassNotFoundException e) { - throw new RuntimeException(e); - } - } - - /** - * Loads the json from the given {@link BufferedReader} into the given {@link Map}. - * @param reader the reader to read the json from - * @param map the map to load into - */ - private static void loadJson(BufferedReader reader, Map<String, JsonElement> map) { - SkyblockerMod.GSON.fromJson(reader, JsonObject.class).asMap().forEach((room, jsonElement) -> map.put(room.toLowerCase().replaceAll(" ", "-"), jsonElement)); - } - - private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, Integer>> markSecretsCommand(boolean found) { - return argument("secret", IntegerArgumentType.integer()).executes(context -> { - int secretIndex = IntegerArgumentType.getInteger(context, "secret"); - if (markSecrets(secretIndex, found)) { - context.getSource().sendFeedback(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFound" : "skyblocker.dungeons.secrets.markSecretMissing", secretIndex)); - } else { - context.getSource().sendError(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFoundUnable" : "skyblocker.dungeons.secrets.markSecretMissingUnable", secretIndex)); - } - return Command.SINGLE_SUCCESS; - }); - } - - /** - * Updates the dungeon. The general idea is similar to the Dungeon Rooms Mod. - * <p></p> - * When entering a new dungeon, this method: - * <ul> - * <li> Gets the physical northwest corner position of the entrance room and saves it in {@link #physicalEntrancePos}. </li> - * <li> Do nothing until the dungeon map exists. </li> - * <li> Gets the upper left corner of entrance room on the map and saves it in {@link #mapEntrancePos}. </li> - * <li> Gets the size of a room on the map in pixels and saves it in {@link #mapRoomSize}. </li> - * <li> Creates a new {@link Room} with {@link Room.Type} {@link Room.Type.ENTRANCE ENTRANCE} and sets {@link #currentRoom}. </li> - * </ul> - * When processing an existing dungeon, this method: - * <ul> - * <li> Calculates the physical northwest corner and upper left corner on the map of the room the player is currently in. </li> - * <li> Gets the room type based on the map color. </li> - * <li> If the room has not been created (when the physical northwest corner is not in {@link #rooms}):</li> - * <ul> - * <li> If the room type is {@link Room.Type.ROOM}, gets the northwest corner of all connected room segments with {@link DungeonMapUtils#getRoomSegments(MapState, Vector2ic, int, byte)}. (For example, a 1x2 room has two room segments.) </li> - * <li> Create a new room. </li> - * </ul> - * <li> Sets {@link #currentRoom} to the current room, either created from the previous step or from {@link #rooms}. </li> - * <li> Calls {@link Room#update()} on {@link #currentRoom}. </li> - * </ul> - */ - @SuppressWarnings("JavadocReference") - private static void update() { - if (!SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints) { - return; - } - if (!Utils.isInDungeons()) { - if (mapEntrancePos != null) { - reset(); - } - return; - } - MinecraftClient client = MinecraftClient.getInstance(); - ClientPlayerEntity player = client.player; - if (player == null || client.world == null) { - return; - } - if (physicalEntrancePos == null) { - Vec3d playerPos = player.getPos(); - physicalEntrancePos = DungeonMapUtils.getPhysicalRoomPos(playerPos); - currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos); - } - ItemStack stack = player.getInventory().main.get(8); - if (!stack.isOf(Items.FILLED_MAP)) { - return; - } - MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world); - if (map == null) { - return; - } - if (mapEntrancePos == null || mapRoomSize == 0) { - ObjectIntPair<Vector2ic> mapEntrancePosAndSize = DungeonMapUtils.getMapEntrancePosAndRoomSize(map); - if (mapEntrancePosAndSize == null) { - return; - } - mapEntrancePos = mapEntrancePosAndSize.left(); - mapRoomSize = mapEntrancePosAndSize.rightInt(); - LOGGER.info("[Skyblocker] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos); - } - - Vector2ic physicalPos = DungeonMapUtils.getPhysicalRoomPos(client.player.getPos()); - Vector2ic mapPos = DungeonMapUtils.getMapPosFromPhysical(physicalEntrancePos, mapEntrancePos, mapRoomSize, physicalPos); - Room room = rooms.get(physicalPos); - if (room == null) { - Room.Type type = DungeonMapUtils.getRoomType(map, mapPos); - if (type == null || type == Room.Type.UNKNOWN) { - return; - } - switch (type) { - case ENTRANCE, PUZZLE, TRAP, MINIBOSS, FAIRY, BLOOD -> room = newRoom(type, physicalPos); - case ROOM -> room = newRoom(type, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, mapPos, mapRoomSize, type.color))); - } - } - if (room != null && currentRoom != room) { - currentRoom = room; - } - currentRoom.update(); - } - - /** - * Creates a new room with the given type and physical positions, - * adds the room to {@link #rooms}, and sets {@link #currentRoom} to the new room. - * - * @param type the type of room to create - * @param physicalPositions the physical positions of the room - */ - @Nullable - private static Room newRoom(Room.Type type, Vector2ic... physicalPositions) { - try { - Room newRoom = new Room(type, physicalPositions); - for (Vector2ic physicalPos : physicalPositions) { - rooms.put(physicalPos, newRoom); - } - return newRoom; - } catch (IllegalArgumentException e) { - LOGGER.error("[Skyblocker] Failed to create room", e); - } - return null; - } - - /** - * Renders the secret waypoints in {@link #currentRoom} if {@link #isCurrentRoomMatched()}. - */ - private static void render(WorldRenderContext context) { - if (isCurrentRoomMatched()) { - currentRoom.render(context); - } - } - - /** - * Calls {@link Room#onChatMessage(String)} on {@link #currentRoom} if the message is an overlay message and {@link #isCurrentRoomMatched()}. - * Used to detect when all secrets in a room are found. - */ - private static void onChatMessage(Text text, boolean overlay) { - if (overlay && isCurrentRoomMatched()) { - currentRoom.onChatMessage(text.getString()); - } - } - - /** - * Calls {@link Room#onUseBlock(World, BlockHitResult)} on {@link #currentRoom} if {@link #isCurrentRoomMatched()}. - * Used to detect finding {@link SecretWaypoint.Category.CHEST} and {@link SecretWaypoint.Category.WITHER} secrets. - * - * @return {@link ActionResult#PASS} - */ - @SuppressWarnings("JavadocReference") - private static ActionResult onUseBlock(World world, BlockHitResult hitResult) { - if (isCurrentRoomMatched()) { - currentRoom.onUseBlock(world, hitResult); - } - return ActionResult.PASS; - } - - /** - * Calls {@link Room#onItemPickup(ItemEntity, LivingEntity)} on the room the {@code collector} is in if that room {@link #isRoomMatched(Room)}. - * Used to detect finding {@link SecretWaypoint.Category.ITEM} secrets. - * If the collector is the player, {@link #currentRoom} is used as an optimization. - */ - @SuppressWarnings("JavadocReference") - public static void onItemPickup(ItemEntity itemEntity, LivingEntity collector, boolean isPlayer) { - if (isPlayer) { - if (isCurrentRoomMatched()) { - currentRoom.onItemPickup(itemEntity, collector); - } - } else { - Room room = getRoomAtPhysical(collector.getPos()); - if (isRoomMatched(room)) { - room.onItemPickup(itemEntity, collector); - } - } - } - - /** - * Calls {@link Room#onBatRemoved(BatEntity)} on the room the {@code bat} is in if that room {@link #isRoomMatched(Room)}. - * Used to detect finding {@link SecretWaypoint.Category.BAT} secrets. - */ - @SuppressWarnings("JavadocReference") - public static void onBatRemoved(AmbientEntity bat) { - Room room = getRoomAtPhysical(bat.getPos()); - if (isRoomMatched(room)) { - room.onBatRemoved(bat); - } - } - - public static boolean markSecrets(int secretIndex, boolean found) { - if (isCurrentRoomMatched()) { - return currentRoom.markSecrets(secretIndex, found); - } - return false; - } - - /** - * Gets the room at the given physical position. - * - * @param pos the physical position - * @return the room at the given physical position, or null if there is no room at the given physical position - * @see #rooms - * @see DungeonMapUtils#getPhysicalRoomPos(Vec3d) - */ - @Nullable - private static Room getRoomAtPhysical(Vec3d pos) { - return rooms.get(DungeonMapUtils.getPhysicalRoomPos(pos)); - } - - /** - * Calls {@link #isRoomMatched(Room)} on {@link #currentRoom}. - * - * @return {@code true} if {@link #currentRoom} is not null and {@link #isRoomMatched(Room)} - */ - private static boolean isCurrentRoomMatched() { - return isRoomMatched(currentRoom); - } - - /** - * Calls {@link #shouldProcess()} and {@link Room#isMatched()} on the given room. - * - * @param room the room to check - * @return {@code true} if {@link #shouldProcess()}, the given room is not null, and {@link Room#isMatched()} on the given room - */ - @Contract("null -> false") - private static boolean isRoomMatched(@Nullable Room room) { - return shouldProcess() && room != null && room.isMatched(); - } - - /** - * Checks if the player is in a dungeon and {@link me.xmrvizzy.skyblocker.config.SkyblockerConfigManager.Dungeons#secretWaypoints Secret Waypoints} is enabled. - * - * @return whether dungeon secrets should be processed - */ - private static boolean shouldProcess() { - return SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints && Utils.isInDungeons(); - } - - /** - * Resets fields when leaving a dungeon. - */ - private static void reset() { - mapEntrancePos = null; - mapRoomSize = 0; - physicalEntrancePos = null; - rooms.clear(); - currentRoom = null; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java deleted file mode 100644 index ae5afaa3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java +++ /dev/null @@ -1,473 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; - -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.ImmutableTable; -import com.google.common.collect.Table; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import it.unimi.dsi.fastutil.ints.IntRBTreeSet; -import it.unimi.dsi.fastutil.ints.IntSortedSet; -import it.unimi.dsi.fastutil.ints.IntSortedSets; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.util.TriState; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.MapColor; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.ItemEntity; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.mob.AmbientEntity; -import net.minecraft.registry.Registries; -import net.minecraft.util.hit.BlockHitResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.MathHelper; -import net.minecraft.world.World; -import org.apache.commons.lang3.tuple.MutableTriple; -import org.apache.commons.lang3.tuple.Triple; -import org.jetbrains.annotations.NotNull; -import org.joml.Vector2i; -import org.joml.Vector2ic; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class Room { - private static final Pattern SECRETS = Pattern.compile("§7(\\d{1,2})/(\\d{1,2}) Secrets"); - @NotNull - private final Type type; - @NotNull - private final Set<Vector2ic> segments; - /** - * The shape of the room. See {@link #getShape(IntSortedSet, IntSortedSet)}. - */ - @NotNull - private final Shape shape; - /** - * The room data containing all rooms for a specific dungeon and {@link #shape}. - */ - private Map<String, int[]> roomsData; - /** - * Contains all possible dungeon rooms for this room. The list is gradually shrunk by checking blocks until only one room is left. - */ - private List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms; - /** - * Contains all blocks that have been checked to prevent checking the same block multiple times. - */ - private Set<BlockPos> checkedBlocks = new HashSet<>(); - /** - * The task that is used to check blocks. This is used to ensure only one such task can run at a time. - */ - private CompletableFuture<Void> findRoom; - private int doubleCheckBlocks; - /** - * Represents the matching state of the room with the following possible values: - * <li>{@link TriState#DEFAULT} means that the room has not been checked, is being processed, or does not {@link Type#needsScanning() need to be processed}. - * <li>{@link TriState#FALSE} means that the room has been checked and there is no match. - * <li>{@link TriState#TRUE} means that the room has been checked and there is a match. - */ - private TriState matched = TriState.DEFAULT; - private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints; - - public Room(@NotNull Type type, @NotNull Vector2ic... physicalPositions) { - this.type = type; - segments = Set.of(physicalPositions); - IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray())); - IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray())); - shape = getShape(segmentsX, segmentsY); - roomsData = DungeonSecrets.ROOMS_DATA.getOrDefault("catacombs", Collections.emptyMap()).getOrDefault(shape.shape.toLowerCase(), Collections.emptyMap()); - possibleRooms = getPossibleRooms(segmentsX, segmentsY); - } - - @NotNull - public Type getType() { - return type; - } - - public boolean isMatched() { - return matched == TriState.TRUE; - } - - @Override - public String toString() { - return "Room{type=" + type + ", shape=" + shape + ", matched=" + matched + ", segments=" + Arrays.toString(segments.toArray()) + "}"; - } - - @NotNull - private Shape getShape(IntSortedSet segmentsX, IntSortedSet segmentsY) { - return switch (segments.size()) { - case 1 -> Shape.ONE_BY_ONE; - case 2 -> Shape.ONE_BY_TWO; - case 3 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.L_SHAPE : Shape.ONE_BY_THREE; - case 4 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.TWO_BY_TWO : Shape.ONE_BY_FOUR; - default -> throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray())); - }; - } - - private List<MutableTriple<Direction, Vector2ic, List<String>>> getPossibleRooms(IntSortedSet segmentsX, IntSortedSet segmentsY) { - List<String> possibleDirectionRooms = new ArrayList<>(roomsData.keySet()); - List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms = new ArrayList<>(); - for (Direction direction : getPossibleDirections(segmentsX, segmentsY)) { - possibleRooms.add(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), possibleDirectionRooms)); - } - return possibleRooms; - } - - @NotNull - private Direction[] getPossibleDirections(IntSortedSet segmentsX, IntSortedSet segmentsY) { - return switch (shape) { - case ONE_BY_ONE, TWO_BY_TWO -> Direction.values(); - case ONE_BY_TWO, ONE_BY_THREE, ONE_BY_FOUR -> { - if (segmentsX.size() > 1 && segmentsY.size() == 1) { - yield new Direction[]{Direction.NW, Direction.SE}; - } else if (segmentsX.size() == 1 && segmentsY.size() > 1) { - yield new Direction[]{Direction.NE, Direction.SW}; - } - throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray())); - } - case L_SHAPE -> { - if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()))) { - yield new Direction[]{Direction.SW}; - } else if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.lastInt()))) { - yield new Direction[]{Direction.SE}; - } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.firstInt()))) { - yield new Direction[]{Direction.NW}; - } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.lastInt()))) { - yield new Direction[]{Direction.NE}; - } - throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray())); - } - }; - } - - /** - * Updates the room. - * <p></p> - * This method returns immediately if any of the following conditions are met: - * <ul> - * <li> The room does not need to be scanned and matched. (When the room is not of type {@link Type.ROOM}, {@link Type.PUZZLE}, or {@link Type.TRAP}. See {@link Type#needsScanning()}) </li> - * <li> The room has been matched or failed to match and is on cooldown. See {@link #matched}. </li> - * <li> {@link #findRoom The previous update} has not completed. </li> - * </ul> - * Then this method tries to match this room through: - * <ul> - * <li> Iterate over a 11 by 11 by 11 box around the player. </li> - * <li> Check it the block is part of this room and not part of a doorway. See {@link #segments} and {@link #notInDoorway(BlockPos)}. </li> - * <li> Checks if the position has been checked and adds it to {@link #checkedBlocks}. </li> - * <li> Calls {@link #checkBlock(ClientWorld, BlockPos)} </li> - * </ul> - */ - @SuppressWarnings("JavadocReference") - protected void update() { - // Logical AND has higher precedence than logical OR - if (!type.needsScanning() || matched != TriState.DEFAULT || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) { - return; - } - MinecraftClient client = MinecraftClient.getInstance(); - ClientPlayerEntity player = client.player; - ClientWorld world = client.world; - if (player == null || world == null) { - return; - } - findRoom = CompletableFuture.runAsync(() -> { - for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) { - if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(world, pos)) { - break; - } - } - }); - } - - private static boolean notInDoorway(BlockPos pos) { - if (pos.getY() < 66 || pos.getY() > 73) { - return true; - } - int x = MathHelper.floorMod(pos.getX() - 8, 32); - int z = MathHelper.floorMod(pos.getZ() - 8, 32); - return (x < 13 || x > 17 || z > 2 && z < 28) && (z < 13 || z > 17 || x > 2 && x < 28); - } - - /** - * Filters out dungeon rooms which does not contain the block at the given position. - * <p></p> - * This method: - * <ul> - * <li> Checks if the block type is included in the dungeon rooms data. See {@link DungeonSecrets#NUMERIC_ID}. </li> - * <li> For each possible direction: </li> - * <ul> - * <li> Rotate and convert the position to a relative position. See {@link DungeonMapUtils#actualToRelative(Direction, Vector2ic, BlockPos)}. </li> - * <li> Encode the block based on the relative position and the custom numeric block id. See {@link #posIdToInt(BlockPos, byte)}. </li> - * <li> For each possible room in the current direction: </li> - * <ul> - * <li> Check if {@link #roomsData} contains the encoded block. </li> - * <li> If so, add the room to the new list of possible rooms for this direction. </li> - * </ul> - * <li> Replace the old possible room list for the current direction with the new one. </li> - * </ul> - * <li> If there are no matching rooms left: </li> - * <ul> - * <li> Terminate matching by setting {@link #matched} to {@link TriState#FALSE}. </li> - * <li> Schedule another matching attempt in 50 ticks (2.5 seconds). </li> - * <li> Reset {@link #possibleRooms} and {@link #checkedBlocks} with {@link #reset()}. </li> - * <li> Return {@code true} </li> - * </ul> - * <li> If there are exactly one room matching: </li> - * <ul> - * <li> Call {@link #roomMatched(String, Direction, Vector2ic)}. </li> - * <li> Discard the no longer needed fields to save memory. </li> - * <li> Return {@code true} </li> - * </ul> - * <li> Return {@code false} </li> - * </ul> - * - * @param world the world to get the block from - * @param pos the position of the block to check - * @return whether room matching should end. Either a match is found or there are no valid rooms left - */ - private boolean checkBlock(ClientWorld world, BlockPos pos) { - byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString()); - if (id == 0) { - return false; - } - for (MutableTriple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) { - int block = posIdToInt(DungeonMapUtils.actualToRelative(directionRooms.getLeft(), directionRooms.getMiddle(), pos), id); - List<String> possibleDirectionRooms = new ArrayList<>(); - for (String room : directionRooms.getRight()) { - if (Arrays.binarySearch(roomsData.get(room), block) >= 0) { - possibleDirectionRooms.add(room); - } - } - directionRooms.setRight(possibleDirectionRooms); - } - - int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum(); - if (matchingRoomsSize == 0) { - // If no rooms match, reset the fields and scan again after 50 ticks. - matched = TriState.FALSE; - DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size()); - Scheduler.INSTANCE.schedule(() -> matched = TriState.DEFAULT, 50); - reset(); - return true; - } else if (matchingRoomsSize == 1 && ++doubleCheckBlocks >= 10) { - // If one room matches, load the secrets for that room and discard the no longer needed fields. - for (Triple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) { - if (directionRooms.getRight().size() == 1) { - roomMatched(directionRooms.getRight().get(0), directionRooms.getLeft(), directionRooms.getMiddle()); - discard(); - return true; - } - } - return false; // This should never happen, we just checked that there is one possible room, and the return true in the loop should activate - } else { - DungeonSecrets.LOGGER.debug("[Skyblocker] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size()); - return false; - } - } - - /** - * Encodes a {@link BlockPos} and the custom numeric block id into an integer. - * - * @param pos the position of the block - * @param id the custom numeric block id - * @return the encoded integer - */ - private int posIdToInt(BlockPos pos, byte id) { - return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id; - } - - /** - * Loads the secret waypoints for the room from {@link DungeonSecrets#waypointsJson} once it has been matched - * and sets {@link #matched} to {@link TriState#TRUE}. - * - * @param directionRooms the direction, position, and name of the room - */ - @SuppressWarnings("JavadocReference") - private void roomMatched(String name, Direction direction, Vector2ic physicalCornerPos) { - Table<Integer, BlockPos, SecretWaypoint> secretWaypointsMutable = HashBasedTable.create(); - for (JsonElement waypointElement : DungeonSecrets.getRoomWaypoints(name)) { - JsonObject waypoint = waypointElement.getAsJsonObject(); - String secretName = waypoint.get("secretName").getAsString(); - int secretIndex = Integer.parseInt(secretName.substring(0, Character.isDigit(secretName.charAt(1)) ? 2 : 1)); - BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint); - secretWaypointsMutable.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos)); - } - secretWaypoints = ImmutableTable.copyOf(secretWaypointsMutable); - matched = TriState.TRUE; - DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size()); - } - - /** - * Resets fields for another round of matching after room matching fails. - */ - private void reset() { - IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray())); - IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray())); - possibleRooms = getPossibleRooms(segmentsX, segmentsY); - checkedBlocks = new HashSet<>(); - doubleCheckBlocks = 0; - } - - /** - * Discards fields after room matching completes when a room is found. - * These fields are no longer needed and are discarded to save memory. - */ - private void discard() { - roomsData = null; - possibleRooms = null; - checkedBlocks = null; - doubleCheckBlocks = 0; - } - - /** - * Calls {@link SecretWaypoint#render(WorldRenderContext)} on {@link #secretWaypoints all secret waypoints}. - */ - protected void render(WorldRenderContext context) { - for (SecretWaypoint secretWaypoint : secretWaypoints.values()) { - if (secretWaypoint.shouldRender()) { - secretWaypoint.render(context); - } - } - } - - /** - * Sets all secrets as found if {@link #isAllSecretsFound(String)}. - */ - protected void onChatMessage(String message) { - if (isAllSecretsFound(message)) { - secretWaypoints.values().forEach(SecretWaypoint::setFound); - } - } - - /** - * Checks if the number of found secrets is equals or greater than the total number of secrets in the room. - * - * @param message the message to check in - * @return whether the number of found secrets is equals or greater than the total number of secrets in the room - */ - protected static boolean isAllSecretsFound(String message) { - Matcher matcher = SECRETS.matcher(message); - if (matcher.find()) { - return Integer.parseInt(matcher.group(1)) >= Integer.parseInt(matcher.group(2)); - } - return false; - } - - /** - * Marks the secret at the interaction position as found when the player interacts with a chest or a player head, - * if there is a secret at the interaction position. - * - * @param world the world to get the block from - * @param hitResult the block being interacted with - * @see #onSecretFound(SecretWaypoint, String, Object...) - */ - protected void onUseBlock(World world, BlockHitResult hitResult) { - BlockState state = world.getBlockState(hitResult.getBlockPos()); - if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) { - secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::needsInteraction).findAny() - .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex)); - } else if (state.isOf(Blocks.LEVER)) { - secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound); - } - } - - /** - * Marks the closest secret that requires item pickup no greater than 6 blocks away as found when the player picks up a secret item. - * - * @param itemEntity the item entity being picked up - * @param collector the collector of the item - * @see #onSecretFound(SecretWaypoint, String, Object...) - */ - protected void onItemPickup(ItemEntity itemEntity, LivingEntity collector) { - if (SecretWaypoint.SECRET_ITEMS.stream().noneMatch(itemEntity.getStack().getName().getString()::contains)) { - return; - } - secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(collector))).filter(SecretWaypoint.getRangePredicate(collector)) - .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); - } - - /** - * Marks the closest bat secret as found when a bat is killed. - * - * @param bat the bat being killed - * @see #onSecretFound(SecretWaypoint, String, Object...) - */ - protected void onBatRemoved(AmbientEntity bat) { - secretWaypoints.values().stream().filter(SecretWaypoint::isBat).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(bat))) - .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex)); - } - - /** - * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found. - * - * @param secretWaypoint the secret waypoint to read the index from. - * @param msg the message to log - * @param args the args for the {@link org.slf4j.Logger#info(String, Object...) Logger#info(String, Object...)} call - */ - private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) { - secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound); - DungeonSecrets.LOGGER.info(msg, args); - } - - protected boolean markSecrets(int secretIndex, boolean found) { - Map<BlockPos, SecretWaypoint> secret = secretWaypoints.row(secretIndex); - if (secret.isEmpty()) { - return false; - } else { - secret.values().forEach(found ? SecretWaypoint::setFound : SecretWaypoint::setMissing); - return true; - } - } - - public enum Type { - ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)), - ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)), - PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)), - TRAP(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.HIGH)), - MINIBOSS(MapColor.YELLOW.getRenderColorByte(MapColor.Brightness.HIGH)), - FAIRY(MapColor.PINK.getRenderColorByte(MapColor.Brightness.HIGH)), - BLOOD(MapColor.BRIGHT_RED.getRenderColorByte(MapColor.Brightness.HIGH)), - UNKNOWN(MapColor.GRAY.getRenderColorByte(MapColor.Brightness.NORMAL)); - final byte color; - - Type(byte color) { - this.color = color; - } - - /** - * @return whether this room type has secrets and needs to be scanned and matched. - */ - private boolean needsScanning() { - return switch (this) { - case ROOM, PUZZLE, TRAP -> true; - default -> false; - }; - } - } - - private enum Shape { - ONE_BY_ONE("1x1"), - ONE_BY_TWO("1x2"), - ONE_BY_THREE("1x3"), - ONE_BY_FOUR("1x4"), - L_SHAPE("L-shape"), - TWO_BY_TWO("2x2"); - final String shape; - - Shape(String shape) { - this.shape = shape; - } - - @Override - public String toString() { - return shape; - } - } - - public enum Direction { - NW, NE, SW, SE - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java deleted file mode 100644 index 73ac1883..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java +++ /dev/null @@ -1,142 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; - -import com.google.gson.JsonObject; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.Entity; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; - -import java.util.List; -import java.util.function.Predicate; -import java.util.function.ToDoubleFunction; - -public class SecretWaypoint { - static final List<String> SECRET_ITEMS = List.of("Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "Treasure Talisman"); - final int secretIndex; - final Category category; - private final Text name; - private final BlockPos pos; - private final Vec3d centerPos; - private boolean missing; - - SecretWaypoint(int secretIndex, JsonObject waypoint, String name, BlockPos pos) { - this.secretIndex = secretIndex; - this.category = Category.get(waypoint); - this.name = Text.of(name); - this.pos = pos; - this.centerPos = pos.toCenterPos(); - this.missing = true; - } - - static ToDoubleFunction<SecretWaypoint> getSquaredDistanceToFunction(Entity entity) { - return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos); - } - - static Predicate<SecretWaypoint> getRangePredicate(Entity entity) { - return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos) <= 36D; - } - - boolean shouldRender() { - return category.isEnabled() && missing; - } - - boolean needsInteraction() { - return category.needsInteraction(); - } - - boolean isLever() { - return category.isLever(); - } - - boolean needsItemPickup() { - return category.needsItemPickup(); - } - - boolean isBat() { - return category.isBat(); - } - - void setFound() { - this.missing = false; - } - - void setMissing() { - this.missing = true; - } - - /** - * Renders the secret waypoint, including a filled cube, a beacon beam, the name, and the distance from the player. - */ - void render(WorldRenderContext context) { - RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, pos, category.colorComponents, 0.5F); - Vec3d posUp = centerPos.add(0, 1, 0); - RenderHelper.renderText(context, name, posUp, true); - double distance = context.camera().getPos().distanceTo(centerPos); - RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true); - } - - enum Category { - ENTRANCE(secretWaypoints -> secretWaypoints.enableEntranceWaypoints, 0, 255, 0), - SUPERBOOM(secretWaypoints -> secretWaypoints.enableSuperboomWaypoints, 255, 0, 0), - CHEST(secretWaypoints -> secretWaypoints.enableChestWaypoints, 2, 213, 250), - ITEM(secretWaypoints -> secretWaypoints.enableItemWaypoints, 2, 64, 250), - BAT(secretWaypoints -> secretWaypoints.enableBatWaypoints, 142, 66, 0), - WITHER(secretWaypoints -> secretWaypoints.enableWitherWaypoints, 30, 30, 30), - LEVER(secretWaypoints -> secretWaypoints.enableLeverWaypoints, 250, 217, 2), - FAIRYSOUL(secretWaypoints -> secretWaypoints.enableFairySoulWaypoints, 255, 85, 255), - STONK(secretWaypoints -> secretWaypoints.enableStonkWaypoints, 146, 52, 235), - DEFAULT(secretWaypoints -> secretWaypoints.enableDefaultWaypoints, 190, 255, 252); - private final Predicate<SkyblockerConfig.SecretWaypoints> enabledPredicate; - private final float[] colorComponents; - - Category(Predicate<SkyblockerConfig.SecretWaypoints> enabledPredicate, int... intColorComponents) { - this.enabledPredicate = enabledPredicate; - colorComponents = new float[intColorComponents.length]; - for (int i = 0; i < intColorComponents.length; i++) { - colorComponents[i] = intColorComponents[i] / 255F; - } - } - - private static Category get(JsonObject categoryJson) { - return switch (categoryJson.get("category").getAsString()) { - case "entrance" -> Category.ENTRANCE; - case "superboom" -> Category.SUPERBOOM; - case "chest" -> Category.CHEST; - case "item" -> Category.ITEM; - case "bat" -> Category.BAT; - case "wither" -> Category.WITHER; - case "lever" -> Category.LEVER; - case "fairysoul" -> Category.FAIRYSOUL; - case "stonk" -> Category.STONK; - default -> Category.DEFAULT; - }; - } - - boolean needsInteraction() { - return this == CHEST || this == WITHER; - } - - boolean isLever() { - return this == LEVER; - } - - boolean needsItemPickup() { - return this == ITEM; - } - - boolean isBat() { - return this == BAT; - } - - boolean isEnabled() { - return enabledPredicate.test(SkyblockerConfigManager.get().locations.dungeons.secretWaypoints); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java deleted file mode 100644 index 787d696e..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java +++ /dev/null @@ -1,72 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.registry.Registries; -import net.minecraft.util.DyeColor; -import net.minecraft.util.Identifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; - - -public class ColorTerminal extends ContainerSolver { - private static final Logger LOGGER = LoggerFactory.getLogger(ColorTerminal.class.getName()); - private static final Map<String, DyeColor> colorFromName; - private DyeColor targetColor; - private static final Map<Item, DyeColor> itemColor; - - public ColorTerminal() { - super("^Select all the ([A-Z ]+) items!$"); - } - - @Override - protected boolean isEnabled() { - targetColor = null; - return SkyblockerConfigManager.get().locations.dungeons.terminals.solveColor; - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { - trimEdges(slots, 6); - List<ColorHighlight> highlights = new ArrayList<>(); - String colorString = groups[0]; - if (targetColor == null) { - targetColor = colorFromName.get(colorString); - if (targetColor == null) { - LOGGER.error("[Skyblocker] Couldn't find dye color corresponding to \"" + colorString + "\""); - return Collections.emptyList(); - } - } - for (Map.Entry<Integer, ItemStack> slot : slots.entrySet()) { - ItemStack itemStack = slot.getValue(); - if (!itemStack.hasEnchantments() && targetColor.equals(itemColor.get(itemStack.getItem()))) { - highlights.add(ColorHighlight.green(slot.getKey())); - } - } - return highlights; - } - - - static { - colorFromName = new HashMap<>(); - for (DyeColor color : DyeColor.values()) - colorFromName.put(color.getName().toUpperCase(Locale.ENGLISH), color); - colorFromName.put("SILVER", DyeColor.LIGHT_GRAY); - colorFromName.put("LIGHT BLUE", DyeColor.LIGHT_BLUE); - - itemColor = new HashMap<>(); - for (DyeColor color : DyeColor.values()) - for (String item : new String[]{"dye", "wool", "stained_glass", "terracotta"}) - itemColor.put(Registries.ITEM.get(new Identifier(color.getName() + '_' + item)), color); - itemColor.put(Items.BONE_MEAL, DyeColor.WHITE); - itemColor.put(Items.LAPIS_LAZULI, DyeColor.BLUE); - itemColor.put(Items.COCOA_BEANS, DyeColor.BROWN); - itemColor.put(Items.INK_SAC, DyeColor.BLACK); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java deleted file mode 100644 index afb07b4b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -public class OrderTerminal extends ContainerSolver { - private final int PANES_NUM = 14; - private int[] orderedSlots; - private int currentNum = Integer.MAX_VALUE; - - public OrderTerminal() { - super("^Click in order!$"); - } - - @Override - protected boolean isEnabled() { - orderedSlots = null; - currentNum = 0; - return SkyblockerConfigManager.get().locations.dungeons.terminals.solveOrder; - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { - if(orderedSlots == null && !orderSlots(slots)) - return Collections.emptyList(); - while(currentNum < PANES_NUM && Items.LIME_STAINED_GLASS_PANE.equals(slots.get(orderedSlots[currentNum]).getItem())) - currentNum++; - List<ColorHighlight> highlights = new ArrayList<>(3); - int last = Integer.min(3, PANES_NUM - currentNum); - for(int i = 0; i < last; i++) { - highlights.add(new ColorHighlight(orderedSlots[currentNum + i], (224 - 64 * i) << 24 | 64 << 16 | 96 << 8 | 255)); - } - return highlights; - } - - public boolean orderSlots(Map<Integer, ItemStack> slots) { - trimEdges(slots, 4); - orderedSlots = new int[PANES_NUM]; - for(Map.Entry<Integer, ItemStack> slot : slots.entrySet()) { - if(Items.AIR.equals(slot.getValue().getItem())) { - orderedSlots = null; - return false; - } - else - orderedSlots[slot.getValue().getCount() - 1] = slot.getKey(); - } - currentNum = 0; - return true; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java deleted file mode 100644 index fa9fa324..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; -import net.minecraft.item.ItemStack; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class StartsWithTerminal extends ContainerSolver { - public StartsWithTerminal() { - super("^What starts with: '([A-Z])'\\?$"); - } - - @Override - protected boolean isEnabled() { - return SkyblockerConfigManager.get().locations.dungeons.terminals.solveStartsWith; - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { - trimEdges(slots, 6); - String prefix = groups[0]; - List<ColorHighlight> highlights = new ArrayList<>(); - for (Map.Entry<Integer, ItemStack> slot : slots.entrySet()) { - ItemStack stack = slot.getValue(); - if (!stack.hasEnchantments() && stack.getName().getString().startsWith(prefix)) { - highlights.add(ColorHighlight.green(slot.getKey())); - } - } - return highlights; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java deleted file mode 100644 index 3d8292a5..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java +++ /dev/null @@ -1,144 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dwarven; - -import it.unimi.dsi.fastutil.ints.IntIntPair; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class DwarvenHud { - - public static final MinecraftClient client = MinecraftClient.getInstance(); - public static List<Commission> commissionList = new ArrayList<>(); - - public static final List<Pattern> COMMISSIONS = Stream.of( - "(?:Titanium|Mithril|Hard Stone) Miner", - "(?:Ice Walker|Goblin|Goblin Raid|Automaton|Sludge|Team Treasurite Member|Yog|Boss Corleone|Thyst) Slayer", - "(?:Lava Springs|Cliffside Veins|Rampart's Quarry|Upper Mines|Royal Mines) Mithril", - "(?:Lava Springs|Cliffside Veins|Rampart's Quarry|Upper Mines|Royal Mines) Titanium", - "Goblin Raid", - "(?:Powder Ghast|Star Sentry) Puncher", - "(?<!Lucky )Raffle", - "Lucky Raffle", - "2x Mithril Powder Collector", - "(?:Ruby|Amber|Sapphire|Jade|Amethyst|Topaz) Gemstone Collector", - "(?:Amber|Sapphire|Jade|Amethyst|Topaz) Crystal Hunter", - "Chest Looter").map(s -> Pattern.compile("^.*(" + s + "): (\\d+\\.?\\d*%|DONE)")) - .collect(Collectors.toList()); - - public static void init() { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("hud") - .then(ClientCommandManager.literal("dwarven") - .executes(Scheduler.queueOpenScreenCommand(DwarvenHudConfigScreen::new)))))); - - HudRenderCallback.EVENT.register((context, tickDelta) -> { - if (!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabled - || client.options.playerListKey.isPressed() - || client.player == null - || commissionList.isEmpty()) { - return; - } - render(HudCommsWidget.INSTANCE, context, SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x, - SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y, commissionList); - }); - } - - public static IntIntPair getDimForConfig(List<Commission> commissions) { - return switch (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.style) { - case SIMPLE -> { - HudCommsWidget.INSTANCE_CFG.updateData(commissions, false); - yield IntIntPair.of( - HudCommsWidget.INSTANCE_CFG.getWidth(), - HudCommsWidget.INSTANCE_CFG.getHeight()); - } - case FANCY -> { - HudCommsWidget.INSTANCE_CFG.updateData(commissions, true); - yield IntIntPair.of( - HudCommsWidget.INSTANCE_CFG.getWidth(), - HudCommsWidget.INSTANCE_CFG.getHeight()); - } - default -> IntIntPair.of(200, 20 * commissions.size()); - }; - } - - public static void render(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List<Commission> commissions) { - - switch (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.style) { - case SIMPLE -> renderSimple(hcw, context, hudX, hudY, commissions); - case FANCY -> renderFancy(hcw, context, hudX, hudY, commissions); - case CLASSIC -> renderClassic(context, hudX, hudY, commissions); - } - } - - public static void renderClassic(DrawContext context, int hudX, int hudY, List<Commission> commissions) { - if (SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enableBackground) { - context.fill(hudX, hudY, hudX + 200, hudY + (20 * commissions.size()), 0x64000000); - } - - int y = 0; - for (Commission commission : commissions) { - context - .drawTextWithShadow(client.textRenderer, - Text.literal(commission.commission + ": ") - .styled(style -> style.withColor(Formatting.AQUA)) - .append(Text.literal(commission.progression) - .styled(style -> style.withColor(Formatting.GREEN))), - hudX + 5, hudY + y + 5, 0xFFFFFFFF); - y += 20; - } - } - - public static void renderSimple(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List<Commission> commissions) { - hcw.updateData(commissions, false); - hcw.update(); - hcw.setX(hudX); - hcw.setY(hudY); - hcw.render(context, - SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enableBackground); - } - - public static void renderFancy(HudCommsWidget hcw, DrawContext context, int hudX, int hudY, List<Commission> commissions) { - hcw.updateData(commissions, true); - hcw.update(); - hcw.setX(hudX); - hcw.setY(hudY); - hcw.render(context, - SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enableBackground); - } - - public static void update() { - commissionList = new ArrayList<>(); - if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabled) - return; - - client.getNetworkHandler().getPlayerList().forEach(playerListEntry -> { - if (playerListEntry.getDisplayName() != null) { - for (Pattern pattern : COMMISSIONS) { - Matcher matcher = pattern.matcher(playerListEntry.getDisplayName().getString()); - if (matcher.find()) { - commissionList.add(new Commission(matcher.group(1), matcher.group(2))); - } - - } - } - }); - } - - // steamroller tactics to get visibility from outside classes (HudCommsWidget) - public record Commission(String commission, String progression) { - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java deleted file mode 100644 index 232817bb..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java +++ /dev/null @@ -1,67 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dwarven; - -import it.unimi.dsi.fastutil.ints.IntIntPair; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud.Commission; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.text.Text; - -import java.awt.*; -import java.util.List; - -public class DwarvenHudConfigScreen extends Screen { - - private static final List<Commission> CFG_COMMS = List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")); - - private int hudX = SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x; - private int hudY = SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y; - private final Screen parent; - - protected DwarvenHudConfigScreen() { - this(null); - } - - public DwarvenHudConfigScreen(Screen parent) { - super(Text.of("Dwarven HUD Config")); - this.parent = parent; - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - renderBackground(context, mouseX, mouseY, delta); - DwarvenHud.render(HudCommsWidget.INSTANCE_CFG, context, hudX, hudY, List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%"))); - context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, height / 2, Color.GRAY.getRGB()); - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - IntIntPair dims = DwarvenHud.getDimForConfig(CFG_COMMS); - if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + 200, hudY + 40) && button == 0) { - hudX = (int) Math.max(Math.min(mouseX - (double) dims.leftInt() / 2, this.width - dims.leftInt()), 0); - hudY = (int) Math.max(Math.min(mouseY - (double) dims.rightInt() / 2, this.height - dims.rightInt()), 0); - } - return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == 1) { - IntIntPair dims = DwarvenHud.getDimForConfig(CFG_COMMS); - hudX = this.width / 2 - dims.leftInt(); - hudY = this.height / 2 - dims.rightInt(); - } - return super.mouseClicked(mouseX, mouseY, button); - } - - @Override - public void close() { - SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x = hudX; - SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y = hudY; - SkyblockerConfigManager.save(); - client.setScreen(parent); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Fetchur.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Fetchur.java deleted file mode 100644 index d5c18545..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Fetchur.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dwarven; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; - -public class Fetchur extends ChatPatternListener { - private static final Map<String, String> answers; - - public Fetchur() { - super("^§e\\[NPC] Fetchur§f: (?:its|theyre) ([a-zA-Z, \\-]*)$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().locations.dwarvenMines.solveFetchur ? ChatFilterResult.FILTER : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - MinecraftClient client = MinecraftClient.getInstance(); - if (client.player == null) return false; - String riddle = matcher.group(1); - String answer = answers.getOrDefault(riddle, riddle); - client.player.sendMessage(Text.of("§e[NPC] Fetchur§f: " + answer), false); - return true; - } - - static { - answers = new HashMap<>(); - answers.put("red and soft", Text.translatable("block.minecraft.red_wool").getString()); - answers.put("yellow and see through", Text.translatable("block.minecraft.yellow_stained_glass").getString()); - answers.put("circular and sometimes moves", Text.translatable("item.minecraft.compass").getString()); - // TODO remove when typo fixed by hypixel - answers.put("circlular and sometimes moves", Text.translatable("item.minecraft.compass").getString()); - answers.put("expensive minerals", "Mithril"); - answers.put("useful during celebrations", Text.translatable("item.minecraft.firework_rocket").getString()); - answers.put("hot and gives energy", "Cheap / Decent Coffee"); - answers.put("tall and can be opened", Text.translatable("block.minecraft.oak_door").getString()); - answers.put("brown and fluffy", Text.translatable("item.minecraft.rabbit_foot").getString()); - answers.put("explosive but more than usual", "Superboom TNT"); - answers.put("wearable and grows", Text.translatable("block.minecraft.pumpkin").getString()); - answers.put("shiny and makes sparks", Text.translatable("item.minecraft.flint_and_steel").getString()); - answers.put("red and white and you can mine it", Text.translatable("block.minecraft.nether_quartz_ore").getString()); - answers.put("round and green, or purple", Text.translatable("item.minecraft.ender_pearl").getString()); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Puzzler.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Puzzler.java deleted file mode 100644 index 90a0e30f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/Puzzler.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dwarven; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; - -import java.util.regex.Matcher; - -public class Puzzler extends ChatPatternListener { - public Puzzler() { - super("^§e\\[NPC] §dPuzzler§f: ((?:§d▲|§5▶|§b◀|§a▼){10})$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().locations.dwarvenMines.solvePuzzler ? null : ChatFilterResult.PASS; - } - - @Override - public boolean onMatch(Text message, Matcher matcher) { - int x = 181; - int z = 135; - for (char c : matcher.group(1).toCharArray()) { - if (c == '▲') z++; - else if (c == '▼') z--; - else if (c == '◀') x++; - else if (c == '▶') x--; - } - ClientWorld world = MinecraftClient.getInstance().world; - if (world != null) - world.setBlockState(new BlockPos(x, 195, z), Blocks.CRIMSON_PLANKS.getDefaultState()); - return false; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java deleted file mode 100644 index 02d612a6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ChronomatronSolver.java +++ /dev/null @@ -1,129 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.experiment; - -import com.google.common.collect.ImmutableMap; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.inventory.Inventory; -import net.minecraft.item.Item; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; - -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class ChronomatronSolver extends ExperimentSolver { - public static final ImmutableMap<Item, Item> TERRACOTTA_TO_GLASS = ImmutableMap.ofEntries( - new AbstractMap.SimpleImmutableEntry<>(Items.RED_TERRACOTTA, Items.RED_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.ORANGE_TERRACOTTA, Items.ORANGE_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.YELLOW_TERRACOTTA, Items.YELLOW_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.LIME_TERRACOTTA, Items.LIME_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.GREEN_TERRACOTTA, Items.GREEN_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.CYAN_TERRACOTTA, Items.CYAN_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.LIGHT_BLUE_TERRACOTTA, Items.LIGHT_BLUE_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.BLUE_TERRACOTTA, Items.BLUE_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.PURPLE_TERRACOTTA, Items.PURPLE_STAINED_GLASS), - new AbstractMap.SimpleImmutableEntry<>(Items.PINK_TERRACOTTA, Items.PINK_STAINED_GLASS) - ); - - private final List<Item> chronomatronSlots = new ArrayList<>(); - private int chronomatronChainLengthCount; - private int chronomatronCurrentSlot; - private int chronomatronCurrentOrdinal; - - public ChronomatronSolver() { - super("^Chronomatron \\(\\w+\\)$"); - } - - public List<Item> getChronomatronSlots() { - return chronomatronSlots; - } - - public int getChronomatronCurrentOrdinal() { - return chronomatronCurrentOrdinal; - } - - public int incrementChronomatronCurrentOrdinal() { - return ++chronomatronCurrentOrdinal; - } - - @Override - protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) { - return experimentsConfig.enableChronomatronSolver; - } - - @Override - protected void tick(Screen screen) { - if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Chronomatron (")) { - switch (getState()) { - case REMEMBER -> { - Inventory inventory = genericContainerScreen.getScreenHandler().getInventory(); - if (chronomatronCurrentSlot == 0) { - for (int index = 10; index < 43; index++) { - if (inventory.getStack(index).hasEnchantments()) { - if (chronomatronSlots.size() <= chronomatronChainLengthCount) { - chronomatronSlots.add(TERRACOTTA_TO_GLASS.get(inventory.getStack(index).getItem())); - setState(State.WAIT); - } else { - chronomatronChainLengthCount++; - } - chronomatronCurrentSlot = index; - return; - } - } - } else if (!inventory.getStack(chronomatronCurrentSlot).hasEnchantments()) { - chronomatronCurrentSlot = 0; - } - } - case WAIT -> { - if (genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString().startsWith("Timer: ")) { - setState(State.SHOW); - } - } - case END -> { - String name = genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString(); - if (!name.startsWith("Timer: ")) { - if (name.equals("Remember the pattern!")) { - chronomatronChainLengthCount = 0; - chronomatronCurrentOrdinal = 0; - setState(State.REMEMBER); - } else { - reset(); - } - } - } - } - } else { - reset(); - } - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { - List<ColorHighlight> highlights = new ArrayList<>(); - if (getState() == State.SHOW && chronomatronSlots.size() > chronomatronCurrentOrdinal) { - for (Map.Entry<Integer, ItemStack> indexStack : slots.entrySet()) { - int index = indexStack.getKey(); - ItemStack stack = indexStack.getValue(); - Item item = chronomatronSlots.get(chronomatronCurrentOrdinal); - if (stack.isOf(item) || TERRACOTTA_TO_GLASS.get(stack.getItem()) == item) { - highlights.add(ColorHighlight.green(index)); - } - } - } - return highlights; - } - - @Override - protected void reset() { - super.reset(); - chronomatronSlots.clear(); - chronomatronChainLengthCount = 0; - chronomatronCurrentSlot = 0; - chronomatronCurrentOrdinal = 0; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java deleted file mode 100644 index 9e25fa2a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/ExperimentSolver.java +++ /dev/null @@ -1,60 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.experiment; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.item.ItemStack; - -import java.util.HashMap; -import java.util.Map; - -public abstract class ExperimentSolver extends ContainerSolver { - public enum State { - REMEMBER, WAIT, SHOW, END - } - - private State state = State.REMEMBER; - private final Map<Integer, ItemStack> slots = new HashMap<>(); - - protected ExperimentSolver(String containerName) { - super(containerName); - } - - public State getState() { - return state; - } - - public void setState(State state) { - this.state = state; - } - - public Map<Integer, ItemStack> getSlots() { - return slots; - } - - @Override - protected final boolean isEnabled() { - return isEnabled(SkyblockerConfigManager.get().general.experiments); - } - - protected abstract boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig); - - @Override - protected void start(GenericContainerScreen screen) { - super.start(screen); - state = State.REMEMBER; - ScreenEvents.afterTick(screen).register(this::tick); - } - - @Override - protected void reset() { - super.reset(); - state = State.REMEMBER; - slots.clear(); - } - - protected abstract void tick(Screen screen); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java deleted file mode 100644 index f329a395..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/SuperpairsSolver.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.experiment; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; - -import java.util.*; - -public class SuperpairsSolver extends ExperimentSolver { - private int superpairsPrevClickedSlot; - private ItemStack superpairsCurrentSlot; - private final Set<Integer> superpairsDuplicatedSlots = new HashSet<>(); - - public SuperpairsSolver() { - super("^Superpairs \\(\\w+\\)$"); - } - - public void setSuperpairsPrevClickedSlot(int superpairsPrevClickedSlot) { - this.superpairsPrevClickedSlot = superpairsPrevClickedSlot; - } - - public void setSuperpairsCurrentSlot(ItemStack superpairsCurrentSlot) { - this.superpairsCurrentSlot = superpairsCurrentSlot; - } - - @Override - protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) { - return experimentsConfig.enableSuperpairsSolver; - } - - @Override - protected void start(GenericContainerScreen screen) { - super.start(screen); - setState(State.SHOW); - } - - @Override - protected void tick(Screen screen) { - if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Superpairs (")) { - if (getState() == State.SHOW) { - if (genericContainerScreen.getScreenHandler().getInventory().getStack(4).isOf(Items.CAULDRON)) { - reset(); - } else if (getSlots().get(superpairsPrevClickedSlot) == null) { - ItemStack itemStack = genericContainerScreen.getScreenHandler().getInventory().getStack(superpairsPrevClickedSlot); - if (!(itemStack.isOf(Items.CYAN_STAINED_GLASS) || itemStack.isOf(Items.BLACK_STAINED_GLASS_PANE) || itemStack.isOf(Items.AIR))) { - getSlots().entrySet().stream().filter((entry -> ItemStack.areEqual(entry.getValue(), itemStack))).findAny().ifPresent(entry -> superpairsDuplicatedSlots.add(entry.getKey())); - getSlots().put(superpairsPrevClickedSlot, itemStack); - superpairsCurrentSlot = itemStack; - } - } - } - } else { - reset(); - } - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> displaySlots) { - List<ColorHighlight> highlights = new ArrayList<>(); - if (getState() == State.SHOW) { - for (Map.Entry<Integer, ItemStack> indexStack : displaySlots.entrySet()) { - int index = indexStack.getKey(); - ItemStack displayStack = indexStack.getValue(); - ItemStack stack = getSlots().get(index); - if (stack != null && !ItemStack.areEqual(stack, displayStack)) { - if (ItemStack.areEqual(superpairsCurrentSlot, stack) && displayStack.getName().getString().equals("Click a second button!")) { - highlights.add(ColorHighlight.green(index)); - } else if (superpairsDuplicatedSlots.contains(index)) { - highlights.add(ColorHighlight.yellow(index)); - } else { - highlights.add(ColorHighlight.red(index)); - } - } - } - } - return highlights; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java deleted file mode 100644 index d4d80ee6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/experiment/UltrasequencerSolver.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.experiment; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.inventory.Inventory; -import net.minecraft.item.ItemStack; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class UltrasequencerSolver extends ExperimentSolver { - private int ultrasequencerNextSlot; - - public UltrasequencerSolver() { - super("^Ultrasequencer \\(\\w+\\)$"); - } - - public int getUltrasequencerNextSlot() { - return ultrasequencerNextSlot; - } - - public void setUltrasequencerNextSlot(int ultrasequencerNextSlot) { - this.ultrasequencerNextSlot = ultrasequencerNextSlot; - } - - @Override - protected boolean isEnabled(SkyblockerConfig.Experiments experimentsConfig) { - return experimentsConfig.enableUltrasequencerSolver; - } - - @Override - protected void tick(Screen screen) { - if (isEnabled() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getTitle().getString().startsWith("Ultrasequencer (")) { - switch (getState()) { - case REMEMBER -> { - Inventory inventory = genericContainerScreen.getScreenHandler().getInventory(); - if (inventory.getStack(49).getName().getString().equals("Remember the pattern!")) { - for (int index = 9; index < 45; index++) { - ItemStack itemStack = inventory.getStack(index); - String name = itemStack.getName().getString(); - if (name.matches("\\d+")) { - if (name.equals("1")) { - ultrasequencerNextSlot = index; - } - getSlots().put(index, itemStack); - } - } - setState(State.WAIT); - } - } - case WAIT -> { - if (genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString().startsWith("Timer: ")) { - setState(State.SHOW); - } - } - case END -> { - String name = genericContainerScreen.getScreenHandler().getInventory().getStack(49).getName().getString(); - if (!name.startsWith("Timer: ")) { - if (name.equals("Remember the pattern!")) { - getSlots().clear(); - setState(State.REMEMBER); - } else { - reset(); - } - } - } - } - } else { - reset(); - } - } - - @Override - protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) { - return getState() == State.SHOW && ultrasequencerNextSlot != 0 ? List.of(ColorHighlight.green(ultrasequencerNextSlot)) : new ArrayList<>(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilter.java deleted file mode 100644 index addb1b05..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class AbilityFilter extends SimpleChatFilter { - public AbilityFilter() { - super("^(?:This ability is on cooldown for " + NUMBER + "s\\.|No more charges, next one in " + NUMBER + "s!)$"); - } - - @Override - protected ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideAbility; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java deleted file mode 100644 index e0493389..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Constants; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.text.Text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class AdFilter extends ChatPatternListener { - private static final Pattern[] AD_FILTERS = new Pattern[] { - Pattern.compile("^(?:i(?:m|'m| am)? |(?:is )?any(?: ?one|1) )?(?:buy|sell|lowball|trade?)(?:ing)?(?:\\W|$)", Pattern.CASE_INSENSITIVE), - Pattern.compile("(.)\\1{7,}"), - Pattern.compile("\\W(?:on|in|check|at) my (?:ah|bin)(?:\\W|$)", Pattern.CASE_INSENSITIVE), }; - - public AdFilter() { - // Groups: - // 1. Player name - // 2. Message - // (?:§8\[[§feadbc0-9]+§8\] )?(?:[§76l]+[<INSERT EMBLEMS>] )?§[67abc](?:\[[§A-Za-z0-9+]+\] )?([A-Za-z0-9_]+)§[f7]: (.+) - super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + Constants.LEVEL_EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)§[f7]: (.+)"); - } - - @Override - public boolean onMatch(Text _message, Matcher matcher) { - String message = matcher.group(2); - for (Pattern adFilter : AD_FILTERS) - if (adFilter.matcher(message).find()) - return true; - return false; - } - - @Override - protected ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideAds; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilter.java deleted file mode 100644 index 02e8867c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class AoteFilter extends SimpleChatFilter { - public AoteFilter() { - super("^There are blocks in the way!$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideAOTE; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilter.java deleted file mode 100644 index 65811a8a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; - -import java.util.Objects; -import java.util.regex.Matcher; - -public class AutopetFilter extends ChatPatternListener { - public AutopetFilter() { - super("^§cAutopet §eequipped your §7.*§e! §a§lVIEW RULE$"); - } - - @Override - public boolean onMatch(Text _message, Matcher matcher) { - if (SkyblockerConfigManager.get().messages.hideAutopet == ChatFilterResult.ACTION_BAR) { - Objects.requireNonNull(MinecraftClient.getInstance().player).sendMessage( - Text.literal( - _message.getString().replace("§a§lVIEW RULE", "") - ), true); - } - return true; - } - - @Override - public ChatFilterResult state() { - if (SkyblockerConfigManager.get().messages.hideAutopet == ChatFilterResult.ACTION_BAR) - return ChatFilterResult.FILTER; - else - return SkyblockerConfigManager.get().messages.hideAutopet; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilter.java deleted file mode 100644 index d884bf37..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilter.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class ComboFilter extends SimpleChatFilter { - public ComboFilter() { - super("^(\\+\\d+ Kill Combo \\+\\d+(% ✯ Magic Find| coins per kill|% Combat Exp)" + - "|Your Kill Combo has expired! You reached a \\d+ Kill Combo!)$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideCombo; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilter.java deleted file mode 100644 index 938b1aaa..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class HealFilter extends SimpleChatFilter { - public HealFilter() { - super("^(?:You healed yourself for " + NUMBER + " health!|[a-zA-Z0-9_]{2,16} healed you for " + NUMBER + " health!)$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideHeal; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilter.java deleted file mode 100644 index 2cc89185..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class ImplosionFilter extends SimpleChatFilter { - public ImplosionFilter() { - super("^Your Implosion hit " + NUMBER + " enem(?:y|ies) for " + NUMBER + " damage\\.$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideImplosion; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/MoltenWaveFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/MoltenWaveFilter.java deleted file mode 100644 index 9627dc13..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/MoltenWaveFilter.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class MoltenWaveFilter extends SimpleChatFilter { - public MoltenWaveFilter() { - super("^Your Molten Wave hit " + NUMBER + " enem(?:y|ies) for " + NUMBER + " damage\\.$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideMoltenWave; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java deleted file mode 100644 index 418e0f27..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/ShowOffFilter.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.utils.Constants; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; - -public class ShowOffFilter extends SimpleChatFilter { - private static final String[] SHOW_TYPES = { "is holding", "is wearing", "is friends with a", "has" }; - - public ShowOffFilter() { - super("(?:§8\\[[§feadbc0-9]+§8\\] )?(?:[§76l]+[" + Constants.LEVEL_EMBLEMS + "] )?§[67abc](?:\\[[§A-Za-z0-9+]+\\] )?([A-Za-z0-9_]+)[§f7]+ (?:" + String.join("|", SHOW_TYPES) + ") §8\\[(.+)§8\\]"); - } - - @Override - protected ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideShowOff; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/SimpleChatFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/SimpleChatFilter.java deleted file mode 100644 index 20017443..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/SimpleChatFilter.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import net.minecraft.text.Text; - -import java.util.regex.Matcher; - -public abstract class SimpleChatFilter extends ChatPatternListener { - public SimpleChatFilter(String pattern) { - super(pattern); - } - - @Override - protected final boolean onMatch(Text message, Matcher matcher) { - return true; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilter.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilter.java deleted file mode 100644 index 2035facb..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilter.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; - -public class TeleportPadFilter extends SimpleChatFilter { - public TeleportPadFilter() { - super("^(Warped from the .* Teleport Pad to the .* Teleport Pad!" + - "|This Teleport Pad does not have a destination set!)$"); - } - - @Override - public ChatFilterResult state() { - return SkyblockerConfigManager.get().messages.hideTeleportPad; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java deleted file mode 100644 index 8f71e7b9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/AttributeShards.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; - -public class AttributeShards { - private static final Object2ObjectOpenHashMap<String, String> ID_2_SHORT_NAME = new Object2ObjectOpenHashMap<>(); - - static { - //Weapons - ID_2_SHORT_NAME.put("arachno", "A"); - ID_2_SHORT_NAME.put("attack_speed", "AS"); - ID_2_SHORT_NAME.put("blazing", "BL"); - ID_2_SHORT_NAME.put("combo", "C"); - ID_2_SHORT_NAME.put("elite", "E"); - ID_2_SHORT_NAME.put("ender", "EN"); - ID_2_SHORT_NAME.put("ignition", "I"); - ID_2_SHORT_NAME.put("life_recovery", "LR"); - ID_2_SHORT_NAME.put("mana_steal", "MS"); - ID_2_SHORT_NAME.put("midas_touch", "MT"); - ID_2_SHORT_NAME.put("undead", "U"); - - //Swords & Bows - ID_2_SHORT_NAME.put("warrior", "W"); - ID_2_SHORT_NAME.put("deadeye", "DE"); - - //Armor or Equipment - ID_2_SHORT_NAME.put("arachno_resistance", "AR"); - ID_2_SHORT_NAME.put("blazing_resistance", "BR"); - ID_2_SHORT_NAME.put("breeze", "B"); - ID_2_SHORT_NAME.put("dominance", "D"); - ID_2_SHORT_NAME.put("ender_resistance", "ER"); - ID_2_SHORT_NAME.put("experience", "XP"); - ID_2_SHORT_NAME.put("fortitude", "F"); - ID_2_SHORT_NAME.put("life_regeneration", "HR"); //Health regeneration - ID_2_SHORT_NAME.put("lifeline", "L"); - ID_2_SHORT_NAME.put("magic_find", "MF"); - ID_2_SHORT_NAME.put("mana_pool", "MP"); - ID_2_SHORT_NAME.put("mana_regeneration", "MR"); - ID_2_SHORT_NAME.put("mending", "V"); //Vitality - ID_2_SHORT_NAME.put("speed", "S"); - ID_2_SHORT_NAME.put("undead_resistance", "UR"); - ID_2_SHORT_NAME.put("veteran", "V"); - - //Fishing Gear - ID_2_SHORT_NAME.put("blazing_fortune", "BF"); - ID_2_SHORT_NAME.put("fishing_experience", "FE"); - ID_2_SHORT_NAME.put("infection", "IF"); - ID_2_SHORT_NAME.put("double_hook", "DH"); - ID_2_SHORT_NAME.put("fisherman", "FM"); - ID_2_SHORT_NAME.put("fishing_speed", "FS"); - ID_2_SHORT_NAME.put("hunter", "H"); - ID_2_SHORT_NAME.put("trophy_hunter", "TH"); - - } - - public static String getShortName(String id) { - return ID_2_SHORT_NAME.getOrDefault(id, ""); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java deleted file mode 100644 index f8af5d33..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/BackpackPreview.java +++ /dev/null @@ -1,235 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.mojang.blaze3d.systems.RenderSystem; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.inventory.Inventory; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.*; -import net.minecraft.util.Identifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class BackpackPreview { - private static final Logger LOGGER = LoggerFactory.getLogger(BackpackPreview.class); - private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png"); - private static final Pattern ECHEST_PATTERN = Pattern.compile("Ender Chest.*\\((\\d+)/\\d+\\)"); - private static final Pattern BACKPACK_PATTERN = Pattern.compile("Backpack.*\\(Slot #(\\d+)\\)"); - private static final int STORAGE_SIZE = 27; - - private static final Inventory[] storage = new Inventory[STORAGE_SIZE]; - private static final boolean[] dirty = new boolean[STORAGE_SIZE]; - - private static String loaded = ""; // uuid + sb profile currently loaded - private static Path save_dir = null; - - public static void init() { - ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { - if (screen instanceof HandledScreen<?> handledScreen) { - updateStorage(handledScreen); - } - }); - } - - public static void tick() { - Utils.update(); // force update isOnSkyblock to prevent crash on disconnect - if (Utils.isOnSkyblock()) { - // save all dirty storages - saveStorage(); - // update save dir based on uuid and sb profile - String uuid = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", ""); - String profile = Utils.getProfile(); - if (profile != null && !profile.isEmpty()) { - save_dir = FabricLoader.getInstance().getConfigDir().resolve("skyblocker/backpack-preview/" + uuid + "/" + profile); - save_dir.toFile().mkdirs(); - if (loaded.equals(uuid + "/" + profile)) { - // mark currently opened storage as dirty - if (MinecraftClient.getInstance().currentScreen != null) { - String title = MinecraftClient.getInstance().currentScreen.getTitle().getString(); - int index = getStorageIndexFromTitle(title); - if (index != -1) dirty[index] = true; - } - } else { - // load storage again because uuid/profile changed - loaded = uuid + "/" + profile; - loadStorage(); - } - } - } - } - - public static void loadStorage() { - assert (save_dir != null); - for (int index = 0; index < STORAGE_SIZE; ++index) { - storage[index] = null; - dirty[index] = false; - File file = save_dir.resolve(index + ".nbt").toFile(); - if (file.isFile()) { - try { - NbtCompound root = NbtIo.read(file); - storage[index] = new DummyInventory(root); - } catch (Exception e) { - LOGGER.error("Failed to load backpack preview file: " + file.getName(), e); - } - } - } - } - - private static void saveStorage() { - assert (save_dir != null); - for (int index = 0; index < STORAGE_SIZE; ++index) { - if (dirty[index]) { - if (storage[index] != null) { - try { - NbtCompound root = new NbtCompound(); - NbtList list = new NbtList(); - for (int i = 9; i < storage[index].size(); ++i) { - ItemStack stack = storage[index].getStack(i); - NbtCompound item = new NbtCompound(); - if (stack.isEmpty()) { - item.put("id", NbtString.of("minecraft:air")); - item.put("Count", NbtInt.of(1)); - } else { - item.put("id", NbtString.of(stack.getItem().toString())); - item.put("Count", NbtInt.of(stack.getCount())); - item.put("tag", stack.getNbt()); - } - list.add(item); - } - root.put("list", list); - root.put("size", NbtInt.of(storage[index].size() - 9)); - NbtIo.write(root, save_dir.resolve(index + ".nbt").toFile()); - dirty[index] = false; - } catch (Exception e) { - LOGGER.error("Failed to save backpack preview file: " + index + ".nbt", e); - } - } - } - } - } - - public static void updateStorage(HandledScreen<?> screen) { - String title = screen.getTitle().getString(); - int index = getStorageIndexFromTitle(title); - if (index != -1) { - storage[index] = screen.getScreenHandler().slots.get(0).inventory; - dirty[index] = true; - } - } - - public static boolean renderPreview(DrawContext context, int index, int mouseX, int mouseY) { - if (index >= 9 && index < 18) index -= 9; - else if (index >= 27 && index < 45) index -= 18; - else return false; - - if (storage[index] == null) return false; - int rows = (storage[index].size() - 9) / 9; - - Screen screen = MinecraftClient.getInstance().currentScreen; - if (screen == null) return false; - int x = mouseX + 184 >= screen.width ? mouseX - 188 : mouseX + 8; - int y = Math.max(0, mouseY - 16); - - RenderSystem.disableDepthTest(); - RenderSystem.setShaderTexture(0, TEXTURE); - context.drawTexture(TEXTURE, x, y, 0, 0, 176, 7); - for (int i = 0; i < rows; ++i) { - context.drawTexture(TEXTURE, x, y + i * 18 + 7, 0, 7, 176, 18); - } - context.drawTexture(TEXTURE, x, y + rows * 18 + 7, 0, 25, 176, 7); - RenderSystem.enableDepthTest(); - - MatrixStack matrices = context.getMatrices(); - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - for (int i = 9; i < storage[index].size(); ++i) { - int itemX = x + (i - 9) % 9 * 18 + 8; - int itemY = y + (i - 9) / 9 * 18 + 8; - matrices.push(); - matrices.translate(0, 0, 200); - context.drawItem(storage[index].getStack(i), itemX, itemY); - context.drawItemInSlot(textRenderer, storage[index].getStack(i), itemX, itemY); - matrices.pop(); - } - - return true; - } - - private static int getStorageIndexFromTitle(String title) { - Matcher echest = ECHEST_PATTERN.matcher(title); - if (echest.find()) return Integer.parseInt(echest.group(1)) - 1; - Matcher backpack = BACKPACK_PATTERN.matcher(title); - if (backpack.find()) return Integer.parseInt(backpack.group(1)) + 8; - return -1; - } -} - -class DummyInventory implements Inventory { - private final List<ItemStack> stacks; - - public DummyInventory(NbtCompound root) { - stacks = new ArrayList<>(root.getInt("size") + 9); - for (int i = 0; i < 9; ++i) stacks.add(ItemStack.EMPTY); - root.getList("list", NbtCompound.COMPOUND_TYPE).forEach(item -> - stacks.add(ItemStack.fromNbt((NbtCompound) item)) - ); - } - - @Override - public int size() { - return stacks.size(); - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public ItemStack getStack(int slot) { - return stacks.get(slot); - } - - @Override - public ItemStack removeStack(int slot, int amount) { - return null; - } - - @Override - public ItemStack removeStack(int slot) { - return null; - } - - @Override - public void setStack(int slot, ItemStack stack) { - stacks.set(slot, stack); - } - - @Override - public void markDirty() { - } - - @Override - public boolean canPlayerUse(PlayerEntity player) { - return false; - } - - @Override - public void clear() { - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java deleted file mode 100644 index 7b93fe1e..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorDeletorPreview.java +++ /dev/null @@ -1,92 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import it.unimi.dsi.fastutil.ints.IntIntPair; -import it.unimi.dsi.fastutil.ints.IntObjectPair; -import me.xmrvizzy.skyblocker.mixin.accessor.DrawContextInvoker; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.tooltip.HoveredTooltipPositioner; -import net.minecraft.client.gui.tooltip.TooltipComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -public class CompactorDeletorPreview { - /** - * The width and height in slots of the compactor/deletor - */ - private static final Map<String, IntIntPair> DIMENSIONS = Map.of( - "4000", IntIntPair.of(1, 1), - "5000", IntIntPair.of(1, 3), - "6000", IntIntPair.of(1, 7), - "7000", IntIntPair.of(2, 6) - ); - private static final IntIntPair DEFAULT_DIMENSION = IntIntPair.of(1, 6); - public static final Pattern NAME = Pattern.compile("PERSONAL_(?<type>COMPACTOR|DELETOR)_(?<size>\\d+)"); - private static final MinecraftClient client = MinecraftClient.getInstance(); - - public static boolean drawPreview(DrawContext context, ItemStack stack, String type, String size, int x, int y) { - List<Text> tooltips = Screen.getTooltipFromItem(client, stack); - int targetIndex = getTargetIndex(tooltips); - if (targetIndex == -1) return false; - - // Get items in compactor or deletor - NbtCompound nbt = stack.getNbt(); - if (nbt == null || !nbt.contains("ExtraAttributes", 10)) { - return false; - } - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - // Get the slots and their items from the nbt, which is in the format personal_compact_<slot_number> or personal_deletor_<slot_number> - List<IntObjectPair<ItemStack>> slots = extraAttributes.getKeys().stream().filter(slot -> slot.contains(type.toLowerCase().substring(0, 7))).map(slot -> IntObjectPair.of(Integer.parseInt(slot.substring(17)), ItemRegistry.getItemStack(extraAttributes.getString(slot)))).toList(); - - List<TooltipComponent> components = tooltips.stream().map(Text::asOrderedText).map(TooltipComponent::of).collect(Collectors.toList()); - IntIntPair dimensions = DIMENSIONS.getOrDefault(size, DEFAULT_DIMENSION); - - // If there are no items in compactor or deletor - if (slots.isEmpty()) { - int slotsCount = dimensions.leftInt() * dimensions.rightInt(); - components.add(targetIndex, TooltipComponent.of(Text.literal(slotsCount + (slotsCount == 1 ? " slot" : " slots")).formatted(Formatting.GRAY).asOrderedText())); - - ((DrawContextInvoker) context).invokeDrawTooltip(client.textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE); - return true; - } - - // Add the preview tooltip component - components.add(targetIndex, new CompactorPreviewTooltipComponent(slots, dimensions)); - - // Render accompanying text - components.add(targetIndex, TooltipComponent.of(Text.literal("Contents:").asOrderedText())); - if (extraAttributes.contains("PERSONAL_DELETOR_ACTIVE")) { - components.add(targetIndex, TooltipComponent.of(Text.literal("Active: ") - .append(extraAttributes.getBoolean("PERSONAL_DELETOR_ACTIVE") ? Text.literal("YES").formatted(Formatting.BOLD).formatted(Formatting.GREEN) : Text.literal("NO").formatted(Formatting.BOLD).formatted(Formatting.RED)).asOrderedText())); - } - ((DrawContextInvoker) context).invokeDrawTooltip(client.textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE); - return true; - } - - /** - * Finds the target index to insert the preview component, which is the second empty line - */ - private static int getTargetIndex(List<Text> tooltips) { - int targetIndex = -1; - int lineCount = 0; - for (int i = 0; i < tooltips.size(); i++) { - if (tooltips.get(i).getString().isEmpty()) { - lineCount += 1; - } - if (lineCount == 2) { - targetIndex = i; - break; - } - } - return targetIndex; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java deleted file mode 100644 index 45e3c635..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import it.unimi.dsi.fastutil.ints.IntIntPair; -import it.unimi.dsi.fastutil.ints.IntObjectPair; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.tooltip.TooltipComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Identifier; - -public class CompactorPreviewTooltipComponent implements TooltipComponent { - private static final Identifier INVENTORY_TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png"); - private final Iterable<IntObjectPair<ItemStack>> items; - private final IntIntPair dimensions; - - public CompactorPreviewTooltipComponent(Iterable<IntObjectPair<ItemStack>> items, IntIntPair dimensions) { - this.items = items; - this.dimensions = dimensions; - } - - @Override - public int getHeight() { - return dimensions.leftInt() * 18 + 14; - } - - @Override - public int getWidth(TextRenderer textRenderer) { - return dimensions.rightInt() * 18 + 14; - } - - @Override - public void drawItems(TextRenderer textRenderer, int x, int y, DrawContext context) { - context.drawTexture(INVENTORY_TEXTURE, x, y, 0, 0, 7 + dimensions.rightInt() * 18, 7); - context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y, 169, 0, 7, 7); - - for (int i = 0; i < dimensions.leftInt(); i++) { - context.drawTexture(INVENTORY_TEXTURE, x, y + 7 + i * 18, 0, 7, 7, 18); - for (int j = 0; j < dimensions.rightInt(); j++) { - context.drawTexture(INVENTORY_TEXTURE, x + 7 + j * 18, y + 7 + i * 18, 7, 7, 18, 18); - } - context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y + 7 + i * 18, 169, 7, 7, 18); - } - context.drawTexture(INVENTORY_TEXTURE, x, y + 7 + dimensions.leftInt() * 18, 0, 25, 7 + dimensions.rightInt() * 18, 7); - context.drawTexture(INVENTORY_TEXTURE, x + 7 + dimensions.rightInt() * 18, y + 7 + dimensions.leftInt() * 18, 169, 25, 7, 7); - - for (IntObjectPair<ItemStack> entry : items) { - int itemX = x + entry.leftInt() % dimensions.rightInt() * 18 + 8; - int itemY = y + entry.leftInt() / dimensions.rightInt() * 18 + 8; - context.drawItem(entry.right(), itemX, itemY); - context.drawItemInSlot(textRenderer, entry.right(), itemX, itemY); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorDyeColors.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorDyeColors.java deleted file mode 100644 index 88df1b40..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorDyeColors.java +++ /dev/null @@ -1,82 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.item.DyeableItem; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; - -public class CustomArmorDyeColors { - public static void init() { - ClientCommandRegistrationCallback.EVENT.register(CustomArmorDyeColors::registerCommands); - } - - private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("custom") - .then(ClientCommandManager.literal("dyeColor") - .executes(context -> customizeDyeColor(context.getSource(), null)) - .then(ClientCommandManager.argument("hexCode", StringArgumentType.string()) - .executes(context -> customizeDyeColor(context.getSource(), StringArgumentType.getString(context, "hexCode"))))))); - } - - @SuppressWarnings("SameReturnValue") - private static int customizeDyeColor(FabricClientCommandSource source, String hex) { - ItemStack heldItem = source.getPlayer().getMainHandStack(); - NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; - - if (hex != null && !isHexadecimalColor(hex)) { - source.sendError(Text.translatable("skyblocker.customDyeColors.invalidHex")); - return Command.SINGLE_SUCCESS; - } - - if (Utils.isOnSkyblock() && heldItem != null) { - if (heldItem.getItem() instanceof DyeableItem) { - if (nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - if (itemUuid != null) { - Object2IntOpenHashMap<String> customDyeColors = SkyblockerConfigManager.get().general.customDyeColors; - - if (hex == null) { - if (customDyeColors.containsKey(itemUuid)) { - customDyeColors.removeInt(itemUuid); - SkyblockerConfigManager.save(); - source.sendFeedback(Text.translatable("skyblocker.customDyeColors.removed")); - } else { - source.sendFeedback(Text.translatable("skyblocker.customDyeColors.neverHad")); - } - } else { - customDyeColors.put(itemUuid, Integer.decode("0x" + hex.replace("#", "")).intValue()); - SkyblockerConfigManager.save(); - source.sendFeedback(Text.translatable("skyblocker.customDyeColors.added")); - } - } else { - source.sendError(Text.translatable("skyblocker.customDyeColors.noItemUuid")); - } - } - } else { - source.sendError(Text.translatable("skyblocker.customDyeColors.notDyeable")); - return Command.SINGLE_SUCCESS; - } - } else { - source.sendError(Text.translatable("skyblocker.customDyeColors.unableToSetColor")); - } - - return Command.SINGLE_SUCCESS; - } - - private static boolean isHexadecimalColor(String s) { - return s.replace("#", "").chars().allMatch(c -> "0123456789ABCDEFabcdef".indexOf(c) >= 0) && s.replace("#", "").length() == 6; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java deleted file mode 100644 index 6eb0623c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomArmorTrims.java +++ /dev/null @@ -1,154 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.suggestion.SuggestionProvider; - -import dev.isxander.yacl3.config.v2.api.SerialEntry; -import it.unimi.dsi.fastutil.Pair; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.events.SkyblockEvents; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -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.client.network.ClientPlayerEntity; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.command.CommandSource; -import net.minecraft.command.argument.IdentifierArgumentType; -import net.minecraft.item.ArmorItem; -import net.minecraft.item.ItemStack; -import net.minecraft.item.trim.ArmorTrim; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtOps; -import net.minecraft.registry.*; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; -import org.jetbrains.annotations.NotNull; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Optional; - -public class CustomArmorTrims { - private static final Logger LOGGER = LoggerFactory.getLogger(CustomArmorTrims.class); - public static final Object2ObjectOpenHashMap<ArmorTrimId, Optional<ArmorTrim>> TRIMS_CACHE = new Object2ObjectOpenHashMap<>(); - private static boolean trimsInitialized = false; - - public static void init() { - SkyblockEvents.JOIN.register(CustomArmorTrims::initializeTrimCache); - ClientCommandRegistrationCallback.EVENT.register(CustomArmorTrims::registerCommand); - } - - private static void initializeTrimCache() { - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (trimsInitialized || player == null) { - return; - } - try { - TRIMS_CACHE.clear(); - DynamicRegistryManager registryManager = player.networkHandler.getRegistryManager(); - for (Identifier material : registryManager.get(RegistryKeys.TRIM_MATERIAL).getIds()) { - for (Identifier pattern : registryManager.get(RegistryKeys.TRIM_PATTERN).getIds()) { - NbtCompound compound = new NbtCompound(); - compound.putString("material", material.toString()); - compound.putString("pattern", pattern.toString()); - - ArmorTrim trim = ArmorTrim.CODEC.parse(RegistryOps.of(NbtOps.INSTANCE, registryManager), compound).resultOrPartial(LOGGER::error).orElse(null); - - // Something went terribly wrong - if (trim == null) throw new IllegalStateException("Trim shouldn't be null! [" + "\"" + material + "\",\"" + pattern + "\"]"); - - TRIMS_CACHE.put(new ArmorTrimId(material, pattern), Optional.of(trim)); - } - } - - LOGGER.info("[Skyblocker] Successfully cached all armor trims!"); - trimsInitialized = true; - } catch (Exception e) { - LOGGER.error("[Skyblocker] Encountered an exception while caching armor trims", e); - } - } - - private static void registerCommand(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("custom") - .then(ClientCommandManager.literal("armorTrim") - .executes(context -> customizeTrim(context.getSource(), null, null)) - .then(ClientCommandManager.argument("material", IdentifierArgumentType.identifier()) - .suggests(getIdSuggestionProvider(RegistryKeys.TRIM_MATERIAL)) - .executes(context -> customizeTrim(context.getSource(), context.getArgument("material", Identifier.class), null)) - .then(ClientCommandManager.argument("pattern", IdentifierArgumentType.identifier()) - .suggests(getIdSuggestionProvider(RegistryKeys.TRIM_PATTERN)) - .executes(context -> customizeTrim(context.getSource(), context.getArgument("material", Identifier.class), context.getArgument("pattern", Identifier.class)))))))); - } - - @NotNull - private static SuggestionProvider<FabricClientCommandSource> getIdSuggestionProvider(RegistryKey<? extends Registry<?>> registryKey) { - return (context, builder) -> context.getSource().listIdSuggestions(registryKey, CommandSource.SuggestedIdType.ELEMENTS, builder, context); - } - - @SuppressWarnings("SameReturnValue") - private static int customizeTrim(FabricClientCommandSource source, Identifier material, Identifier pattern) { - ItemStack heldItem = source.getPlayer().getMainHandStack(); - NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; - - if (Utils.isOnSkyblock() && heldItem != null) { - if (heldItem.getItem() instanceof ArmorItem) { - if (nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - if (itemUuid != null) { - Object2ObjectOpenHashMap<String, ArmorTrimId> customArmorTrims = SkyblockerConfigManager.get().general.customArmorTrims; - - if (material == null && pattern == null) { - if (customArmorTrims.containsKey(itemUuid)) { - customArmorTrims.remove(itemUuid); - SkyblockerConfigManager.save(); - source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.removed")); - } else { - source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.neverHad")); - } - } else { - // Ensure that the material & trim are valid - ArmorTrimId trimId = new ArmorTrimId(material, pattern); - if (TRIMS_CACHE.get(trimId) == null) { - source.sendError(Text.translatable("skyblocker.customArmorTrims.invalidMaterialOrPattern")); - - return Command.SINGLE_SUCCESS; - } - - customArmorTrims.put(itemUuid, trimId); - SkyblockerConfigManager.save(); - source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.added")); - } - } else { - source.sendError(Text.translatable("skyblocker.customArmorTrims.noItemUuid")); - } - } - } else { - source.sendError(Text.translatable("skyblocker.customArmorTrims.notAnArmorPiece")); - return Command.SINGLE_SUCCESS; - } - } else { - source.sendError(Text.translatable("skyblocker.customArmorTrims.unableToSetTrim")); - } - - return Command.SINGLE_SUCCESS; - } - - public record ArmorTrimId(@SerialEntry Identifier material, @SerialEntry Identifier pattern) implements Pair<Identifier, Identifier> { - @Override - public Identifier left() { - return material(); - } - - @Override - public Identifier right() { - return pattern(); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java deleted file mode 100644 index 76312461..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/CustomItemNames.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.command.argument.TextArgumentType; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.MutableText; -import net.minecraft.text.Style; -import net.minecraft.text.Text; - -public class CustomItemNames { - public static void init() { - ClientCommandRegistrationCallback.EVENT.register(CustomItemNames::registerCommands); - } - - private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("custom") - .then(ClientCommandManager.literal("renameItem") - .executes(context -> renameItem(context.getSource(), null)) - .then(ClientCommandManager.argument("textComponent", TextArgumentType.text()) - .executes(context -> renameItem(context.getSource(), context.getArgument("textComponent", Text.class))))))); - } - - @SuppressWarnings("SameReturnValue") - private static int renameItem(FabricClientCommandSource source, Text text) { - ItemStack heldItem = source.getPlayer().getMainHandStack(); - NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; - - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - if (itemUuid != null) { - Object2ObjectOpenHashMap<String, Text> customItemNames = SkyblockerConfigManager.get().general.customItemNames; - - if (text == null) { - if (customItemNames.containsKey(itemUuid)) { - //Remove custom item name when the text argument isn't passed - customItemNames.remove(itemUuid); - SkyblockerConfigManager.save(); - source.sendFeedback(Text.translatable("skyblocker.customItemNames.removed")); - } else { - source.sendFeedback(Text.translatable("skyblocker.customItemNames.neverHad")); - } - } else { - //If the text is provided then set the item's custom name to it - - //Set italic to false if it hasn't been changed (or was already false) - Style currentStyle = text.getStyle(); - ((MutableText) text).setStyle(currentStyle.withItalic((currentStyle.isItalic() ? true : false))); - - customItemNames.put(itemUuid, text); - SkyblockerConfigManager.save(); - source.sendFeedback(Text.translatable("skyblocker.customItemNames.added")); - } - } else { - source.sendError(Text.translatable("skyblocker.customItemNames.noItemUuid")); - } - } else { - source.sendError(Text.translatable("skyblocker.customItemNames.unableToSetName")); - } - - return Command.SINGLE_SUCCESS; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java deleted file mode 100644 index dbe0c16e..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemCooldowns.java +++ /dev/null @@ -1,115 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.google.common.collect.ImmutableList; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.events.ClientPlayerBlockBreakEvent; -import me.xmrvizzy.skyblocker.utils.ItemUtils; -import net.fabricmc.fabric.api.event.player.UseItemCallback; -import net.minecraft.block.BlockState; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.registry.tag.BlockTags; -import net.minecraft.util.Hand; -import net.minecraft.util.TypedActionResult; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; - -import java.util.HashMap; -import java.util.Map; - -public class ItemCooldowns { - private static final String JUNGLE_AXE_ID = "JUNGLE_AXE"; - private static final String TREECAPITATOR_ID = "TREECAPITATOR_AXE"; - private static final String GRAPPLING_HOOK_ID = "GRAPPLING_HOOK"; - private static final ImmutableList<String> BAT_ARMOR_IDS = ImmutableList.of("BAT_PERSON_HELMET", "BAT_PERSON_CHESTPLATE", "BAT_PERSON_LEGGINGS", "BAT_PERSON_BOOTS"); - - private static final Map<String, CooldownEntry> ITEM_COOLDOWNS = new HashMap<>(); - - public static void init() { - ClientPlayerBlockBreakEvent.AFTER.register(ItemCooldowns::afterBlockBreak); - UseItemCallback.EVENT.register(ItemCooldowns::onItemInteract); - } - - public static void afterBlockBreak(World world, PlayerEntity player, BlockPos pos, BlockState state) { - if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return; - - String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); - if (usedItemId == null) return; - - if (state.isIn(BlockTags.LOGS)) { - if (usedItemId.equals(JUNGLE_AXE_ID)) { - if (!isOnCooldown(JUNGLE_AXE_ID)) { - ITEM_COOLDOWNS.put(JUNGLE_AXE_ID, new CooldownEntry(2000)); - } - } else if (usedItemId.equals(TREECAPITATOR_ID)) { - if (!isOnCooldown(TREECAPITATOR_ID)) { - ITEM_COOLDOWNS.put(TREECAPITATOR_ID, new CooldownEntry(2000)); - } - } - } - } - - private static TypedActionResult<ItemStack> onItemInteract(PlayerEntity player, World world, Hand hand) { - if (!SkyblockerConfigManager.get().general.itemCooldown.enableItemCooldowns) return TypedActionResult.pass(ItemStack.EMPTY); - - String usedItemId = ItemUtils.getItemId(player.getMainHandStack()); - if (usedItemId != null && usedItemId.equals(GRAPPLING_HOOK_ID) && player.fishHook != null) { - if (!isOnCooldown(GRAPPLING_HOOK_ID) && !isWearingBatArmor(player)) { - ITEM_COOLDOWNS.put(GRAPPLING_HOOK_ID, new CooldownEntry(2000)); - } - } - - return TypedActionResult.pass(ItemStack.EMPTY); - } - - public static boolean isOnCooldown(ItemStack itemStack) { - return isOnCooldown(ItemUtils.getItemId(itemStack)); - } - - private static boolean isOnCooldown(String itemId) { - if (ITEM_COOLDOWNS.containsKey(itemId)) { - CooldownEntry cooldownEntry = ITEM_COOLDOWNS.get(itemId); - if (cooldownEntry.isOnCooldown()) { - return true; - } else { - ITEM_COOLDOWNS.remove(itemId); - return false; - } - } - - return false; - } - - public static CooldownEntry getItemCooldownEntry(ItemStack itemStack) { - return ITEM_COOLDOWNS.get(ItemUtils.getItemId(itemStack)); - } - - private static boolean isWearingBatArmor(PlayerEntity player) { - for (ItemStack stack : player.getArmorItems()) { - String itemId = ItemUtils.getItemId(stack); - if (!BAT_ARMOR_IDS.contains(itemId)) { - return false; - } - } - return true; - } - - public record CooldownEntry(int cooldown, long startTime) { - public CooldownEntry(int cooldown) { - this(cooldown, System.currentTimeMillis()); - } - - public boolean isOnCooldown() { - return (this.startTime + this.cooldown) > System.currentTimeMillis(); - } - - public long getRemainingCooldown() { - long time = (this.startTime + this.cooldown) - System.currentTimeMillis(); - return Math.max(time, 0); - } - - public float getRemainingCooldownPercent() { - return this.isOnCooldown() ? (float) this.getRemainingCooldown() / cooldown : 0.0f; - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemProtection.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemProtection.java deleted file mode 100644 index db671787..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemProtection.java +++ /dev/null @@ -1,75 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; - -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; - -public class ItemProtection { - - public static void init() { - ClientCommandRegistrationCallback.EVENT.register(ItemProtection::registerCommand); - } - - public static boolean isItemProtected(ItemStack stack) { - if (stack == null || stack.isEmpty()) return false; - - NbtCompound nbt = stack.getNbt(); - - if (nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : ""; - - return SkyblockerConfigManager.get().general.protectedItems.contains(itemUuid); - } - - return false; - } - - private static void registerCommand(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("protectItem") - .executes(context -> protectMyItem(context.getSource())))); - } - - private static int protectMyItem(FabricClientCommandSource source) { - ItemStack heldItem = source.getPlayer().getMainHandStack(); - NbtCompound nbt = (heldItem != null) ? heldItem.getNbt() : null; - - if (Utils.isOnSkyblock() && nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.contains("uuid") ? extraAttributes.getString("uuid") : null; - - if (itemUuid != null) { - ObjectOpenHashSet<String> protectedItems = SkyblockerConfigManager.get().general.protectedItems; - - if (!protectedItems.contains(itemUuid)) { - protectedItems.add(itemUuid); - SkyblockerConfigManager.save(); - - source.sendFeedback(Text.translatable("skyblocker.itemProtection.added", heldItem.getName())); - } else { - protectedItems.remove(itemUuid); - SkyblockerConfigManager.save(); - - source.sendFeedback(Text.translatable("skyblocker.itemProtection.removed", heldItem.getName())); - } - } else { - source.sendFeedback(Text.translatable("skyblocker.itemProtection.noItemUuid")); - } - } else { - source.sendFeedback(Text.translatable("skyblocker.itemProtection.unableToProtect")); - } - - return Command.SINGLE_SUCCESS; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java deleted file mode 100644 index 837c209a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/ItemRarityBackgrounds.java +++ /dev/null @@ -1,109 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; - -import com.google.common.collect.ImmutableMap; -import com.mojang.blaze3d.systems.RenderSystem; - -import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.item.TooltipContext; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.texture.Sprite; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -public class ItemRarityBackgrounds { - private static final Identifier RARITY_BG_TEX = new Identifier(SkyblockerMod.NAMESPACE, "item_rarity_background"); - private static final Supplier<Sprite> SPRITE = () -> MinecraftClient.getInstance().getGuiAtlasManager().getSprite(RARITY_BG_TEX); - private static final ImmutableMap<String, SkyblockItemRarity> LORE_RARITIES = ImmutableMap.ofEntries( - Map.entry("ADMIN", SkyblockItemRarity.ADMIN), - Map.entry("SPECIAL", SkyblockItemRarity.SPECIAL), //Very special is the same color so this will cover it - Map.entry("DIVINE", SkyblockItemRarity.DIVINE), - Map.entry("MYTHIC", SkyblockItemRarity.MYTHIC), - Map.entry("LEGENDARY", SkyblockItemRarity.LEGENDARY), - Map.entry("LEGENJERRY", SkyblockItemRarity.LEGENDARY), - Map.entry("EPIC", SkyblockItemRarity.EPIC), - Map.entry("RARE", SkyblockItemRarity.RARE), - Map.entry("UNCOMMON", SkyblockItemRarity.UNCOMMON), - Map.entry("COMMON", SkyblockItemRarity.COMMON) - ); - private static final Int2ReferenceOpenHashMap<SkyblockItemRarity> CACHE = new Int2ReferenceOpenHashMap<>(); - - public static void init() { - //Clear the cache every 5 minutes, ints are very compact! - Scheduler.INSTANCE.scheduleCyclic(CACHE::clear, 4800); - - //Clear cache after a screen where items can be upgraded in rarity closes - ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { - String title = screen.getTitle().getString(); - - if (Utils.isOnSkyblock() && (title.equals("The Hex") || title.equals("Craft Item") || title.equals("Anvil") || title.equals("Reforge Anvil"))) { - ScreenEvents.remove(screen).register(screen1 -> CACHE.clear()); - } - }); - } - - public static void tryDraw(ItemStack stack, DrawContext context, int x, int y) { - MinecraftClient client = MinecraftClient.getInstance(); - - if (client.player != null) { - SkyblockItemRarity itemRarity = getItemRarity(stack, client.player); - - if (itemRarity != null) draw(context, x, y, itemRarity); - } - } - - private static SkyblockItemRarity getItemRarity(ItemStack stack, ClientPlayerEntity player) { - if (stack == null || stack.isEmpty()) return null; - - int hashCode = 0; - NbtCompound nbt = stack.getNbt(); - - if (nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - String itemUuid = extraAttributes.getString("uuid"); - - //If the item has an uuid, then use the hash code of the uuid otherwise use the identity hash code of the stack - hashCode = itemUuid.isEmpty() ? System.identityHashCode(stack) : itemUuid.hashCode(); - } - - if (CACHE.containsKey(hashCode)) return CACHE.get(hashCode); - - List<Text> tooltip = stack.getTooltip(player, TooltipContext.BASIC); - String[] stringifiedTooltip = tooltip.stream().map(Text::getString).toArray(String[]::new); - - for (String rarityString : LORE_RARITIES.keySet()) { - if (Arrays.stream(stringifiedTooltip).anyMatch(line -> line.contains(rarityString))) { - SkyblockItemRarity rarity = LORE_RARITIES.get(rarityString); - - CACHE.put(hashCode, rarity); - return rarity; - } - } - - CACHE.put(hashCode, null); - return null; - } - - private static void draw(DrawContext context, int x, int y, SkyblockItemRarity rarity) { - //Enable blending to handle HUD translucency - RenderSystem.enableBlend(); - RenderSystem.defaultBlendFunc(); - - context.drawSprite(x, y, 0, 16, 16, SPRITE.get(), rarity.r, rarity.g, rarity.b, SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgroundsOpacity); - - RenderSystem.disableBlend(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java deleted file mode 100644 index 16a4c596..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/PriceInfoTooltip.java +++ /dev/null @@ -1,443 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.google.gson.Gson; -import com.google.gson.JsonObject; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Http; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.item.TooltipContext; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.net.http.HttpHeaders; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; - -public class PriceInfoTooltip { - private static final Logger LOGGER = LoggerFactory.getLogger(PriceInfoTooltip.class.getName()); - private static final MinecraftClient client = MinecraftClient.getInstance(); - private static JsonObject npcPricesJson; - private static JsonObject bazaarPricesJson; - private static JsonObject oneDayAvgPricesJson; - private static JsonObject threeDayAvgPricesJson; - private static JsonObject lowestPricesJson; - private static JsonObject isMuseumJson; - private static JsonObject motesPricesJson; - private static boolean nullMsgSend = false; - private final static Gson gson = new Gson(); - private static final Map<String, String> apiAddresses; - private static long npcHash = 0; - private static long museumHash = 0; - private static long motesHash = 0; - - public static void onInjectTooltip(ItemStack stack, TooltipContext context, List<Text> lines) { - if (!Utils.isOnSkyblock() || client.player == null) return; - - String name = getInternalNameFromNBT(stack, false); - String internalID = getInternalNameFromNBT(stack, true); - String neuName = name; - if (name == null || internalID == null) return; - - if(name.startsWith("ISSHINY_")){ - name = "SHINY_" + internalID; - neuName = internalID; - } - - int count = stack.getCount(); - boolean bazaarOpened = lines.stream().anyMatch(each -> each.getString().contains("Buy price:") || each.getString().contains("Sell price:")); - - if (SkyblockerConfigManager.get().general.itemTooltip.enableNPCPrice) { - if (npcPricesJson == null) { - nullWarning(); - } else if (npcPricesJson.has(internalID)) { - lines.add(Text.literal(String.format("%-21s", "NPC Price:")) - .formatted(Formatting.YELLOW) - .append(getCoinsMessage(npcPricesJson.get(internalID).getAsDouble(), count))); - } - } - - if (SkyblockerConfigManager.get().general.itemTooltip.enableMotesPrice && Utils.isInTheRift()) { - if (motesPricesJson == null) { - nullWarning(); - } else if (motesPricesJson.has(internalID)) { - lines.add(Text.literal(String.format("%-20s", "Motes Price:")) - .formatted(Formatting.LIGHT_PURPLE) - .append(getMotesMessage(motesPricesJson.get(internalID).getAsInt(), count))); - } - } - - boolean bazaarExist = false; - - if (SkyblockerConfigManager.get().general.itemTooltip.enableBazaarPrice && !bazaarOpened) { - if (bazaarPricesJson == null) { - nullWarning(); - } else if (bazaarPricesJson.has(name)) { - JsonObject getItem = bazaarPricesJson.getAsJsonObject(name); - lines.add(Text.literal(String.format("%-18s", "Bazaar buy Price:")) - .formatted(Formatting.GOLD) - .append(getItem.get("buyPrice").isJsonNull() - ? Text.literal("No data").formatted(Formatting.RED) - : getCoinsMessage(getItem.get("buyPrice").getAsDouble(), count))); - lines.add(Text.literal(String.format("%-19s", "Bazaar sell Price:")) - .formatted(Formatting.GOLD) - .append(getItem.get("sellPrice").isJsonNull() - ? Text.literal("No data").formatted(Formatting.RED) - : getCoinsMessage(getItem.get("sellPrice").getAsDouble(), count))); - bazaarExist = true; - } - } - - // bazaarOpened & bazaarExist check for lbin, because Skytils keeps some bazaar item data in lbin api - boolean lbinExist = false; - if (SkyblockerConfigManager.get().general.itemTooltip.enableLowestBIN && !bazaarOpened && !bazaarExist) { - if (lowestPricesJson == null) { - nullWarning(); - } else if (lowestPricesJson.has(name)) { - lines.add(Text.literal(String.format("%-19s", "Lowest BIN Price:")) - .formatted(Formatting.GOLD) - .append(getCoinsMessage(lowestPricesJson.get(name).getAsDouble(), count))); - lbinExist = true; - } - } - - if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) { - if (threeDayAvgPricesJson == null || oneDayAvgPricesJson == null) { - nullWarning(); - } else { - /* - We are skipping check average prices for potions, runes - and enchanted books because there is no data for their in API. - */ - switch (internalID) { - case "PET" -> { - neuName = neuName.replaceAll("LVL_\\d*_", ""); - String[] parts = neuName.split("_"); - String type = parts[0]; - neuName = neuName.replaceAll(type + "_", ""); - neuName = neuName + "-" + type; - neuName = neuName.replace("UNCOMMON", "1") - .replace("COMMON", "0") - .replace("RARE", "2") - .replace("EPIC", "3") - .replace("LEGENDARY", "4") - .replace("MYTHIC", "5") - .replace("-", ";"); - } - case "RUNE" -> neuName = neuName.replaceAll("_(?!.*_)", ";"); - case "POTION" -> neuName = ""; - case "ATTRIBUTE_SHARD" -> - neuName = internalID + "+" + neuName.replace("SHARD-", "").replaceAll("_(?!.*_)", ";"); - default -> neuName = neuName.replace(":", "-"); - } - - if (!neuName.isEmpty() && lbinExist) { - SkyblockerConfig.Average type = SkyblockerConfigManager.get().general.itemTooltip.avg; - - // "No data" line because of API not keeping old data, it causes NullPointerException - if (type == SkyblockerConfig.Average.ONE_DAY || type == SkyblockerConfig.Average.BOTH) { - lines.add( - Text.literal(String.format("%-19s", "1 Day Avg. Price:")) - .formatted(Formatting.GOLD) - .append(oneDayAvgPricesJson.get(neuName) == null - ? Text.literal("No data").formatted(Formatting.RED) - : getCoinsMessage(oneDayAvgPricesJson.get(neuName).getAsDouble(), count) - ) - ); - } - if (type == SkyblockerConfig.Average.THREE_DAY || type == SkyblockerConfig.Average.BOTH) { - lines.add( - Text.literal(String.format("%-19s", "3 Day Avg. Price:")) - .formatted(Formatting.GOLD) - .append(threeDayAvgPricesJson.get(neuName) == null - ? Text.literal("No data").formatted(Formatting.RED) - : getCoinsMessage(threeDayAvgPricesJson.get(neuName).getAsDouble(), count) - ) - ); - } - } - } - } - - if (SkyblockerConfigManager.get().general.itemTooltip.enableMuseumDate && !bazaarOpened) { - if (isMuseumJson == null) { - nullWarning(); - } else { - String timestamp = getTimestamp(stack); - - if (isMuseumJson.has(internalID)) { - String itemCategory = isMuseumJson.get(internalID).getAsString(); - String format = switch (itemCategory) { - case "Weapons" -> "%-18s"; - case "Armor" -> "%-19s"; - default -> "%-20s"; - }; - lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")")) - .formatted(Formatting.LIGHT_PURPLE) - .append(Text.literal(timestamp).formatted(Formatting.RED))); - } else if (!timestamp.isEmpty()) { - lines.add(Text.literal(String.format("%-21s", "Obtained: ")) - .formatted(Formatting.LIGHT_PURPLE) - .append(Text.literal(timestamp).formatted(Formatting.RED))); - } - } - } - } - - private static void nullWarning() { - if (!nullMsgSend && client.player != null) { - client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false); - nullMsgSend = true; - } - } - - public static NbtCompound getItemNBT(ItemStack stack) { - if (stack == null) return null; - return stack.getNbt(); - } - - /** - * this method converts the "timestamp" variable into the same date format as Hypixel represents it in the museum. - * Currently, there are two types of timestamps the legacy which is built like this - * "dd/MM/yy hh:mm" ("25/04/20 16:38") and the current which is built like this - * "MM/dd/yy hh:mm aa" ("12/24/20 11:08 PM"). Since Hypixel transforms the two formats into one format without - * taking into account of their formats, we do the same. The final result looks like this - * "MMMM dd, yyyy" (December 24, 2020). - * Since the legacy format has a 25 as "month" SimpleDateFormat converts the 25 into 2 years and 1 month and makes - * "25/04/20 16:38" -> "January 04, 2022" instead of "April 25, 2020". - * This causes the museum rank to be much worse than it should be. - * - * @param stack the item under the pointer - * @return if the item have a "Timestamp" it will be shown formated on the tooltip - */ - public static String getTimestamp(ItemStack stack) { - NbtCompound tag = getItemNBT(stack); - - if (tag != null && tag.contains("ExtraAttributes", 10)) { - NbtCompound ea = tag.getCompound("ExtraAttributes"); - - if (ea.contains("timestamp", 8)) { - SimpleDateFormat nbtFormat = new SimpleDateFormat("MM/dd/yy"); - - try { - Date date = nbtFormat.parse(ea.getString("timestamp")); - SimpleDateFormat skyblockerFormat = new SimpleDateFormat("MMMM dd, yyyy", Locale.ENGLISH); - return skyblockerFormat.format(date); - } catch (ParseException e) { - LOGGER.warn("[Skyblocker-tooltip] getTimestamp", e); - } - } - } - - return ""; - } - - public static String getInternalNameFromNBT(ItemStack stack, boolean internalIDOnly) { - NbtCompound tag = getItemNBT(stack); - if (tag == null || !tag.contains("ExtraAttributes", 10)) { - return null; - } - NbtCompound ea = tag.getCompound("ExtraAttributes"); - - if (!ea.contains("id", 8)) { - return null; - } - String internalName = ea.getString("id"); - - if (internalIDOnly) { - return internalName; - } - - // Transformation to API format. - if (ea.contains("is_shiny")){ - return "ISSHINY_" + internalName; - } - - switch (internalName) { - case "ENCHANTED_BOOK" -> { - if (ea.contains("enchantments")) { - NbtCompound enchants = ea.getCompound("enchantments"); - Optional<String> firstEnchant = enchants.getKeys().stream().findFirst(); - String enchant = firstEnchant.orElse(""); - return "ENCHANTMENT_" + enchant.toUpperCase(Locale.ENGLISH) + "_" + enchants.getInt(enchant); - } - } - case "PET" -> { - if (ea.contains("petInfo")) { - JsonObject petInfo = gson.fromJson(ea.getString("petInfo"), JsonObject.class); - return "LVL_1_" + petInfo.get("tier").getAsString() + "_" + petInfo.get("type").getAsString(); - } - } - case "POTION" -> { - String enhanced = ea.contains("enhanced") ? "_ENHANCED" : ""; - String extended = ea.contains("extended") ? "_EXTENDED" : ""; - String splash = ea.contains("splash") ? "_SPLASH" : ""; - if (ea.contains("potion") && ea.contains("potion_level")) { - return (ea.getString("potion") + "_" + internalName + "_" + ea.getInt("potion_level") - + enhanced + extended + splash).toUpperCase(Locale.ENGLISH); - } - } - case "RUNE" -> { - if (ea.contains("runes")) { - NbtCompound runes = ea.getCompound("runes"); - Optional<String> firstRunes = runes.getKeys().stream().findFirst(); - String rune = firstRunes.orElse(""); - return rune.toUpperCase(Locale.ENGLISH) + "_RUNE_" + runes.getInt(rune); - } - } - case "ATTRIBUTE_SHARD" -> { - if (ea.contains("attributes")) { - NbtCompound shards = ea.getCompound("attributes"); - Optional<String> firstShards = shards.getKeys().stream().findFirst(); - String shard = firstShards.orElse(""); - return internalName + "-" + shard.toUpperCase(Locale.ENGLISH) + "_" + shards.getInt(shard); - } - } - } - return internalName; - } - - - private static Text getCoinsMessage(double price, int count) { - // Format the price string once - String priceString = String.format(Locale.ENGLISH, "%1$,.1f", price); - - // If count is 1, return a simple message - if (count == 1) { - return Text.literal(priceString + " Coins").formatted(Formatting.DARK_AQUA); - } - - // If count is greater than 1, include the "each" information - String priceStringTotal = String.format(Locale.ENGLISH, "%1$,.1f", price * count); - MutableText message = Text.literal(priceStringTotal + " Coins ").formatted(Formatting.DARK_AQUA); - message.append(Text.literal("(" + priceString + " each)").formatted(Formatting.GRAY)); - - return message; - } - - private static Text getMotesMessage(int price, int count) { - float motesMultiplier = SkyblockerConfigManager.get().locations.rift.mcGrubberStacks * 0.05f + 1; - - // Calculate the total price - int totalPrice = price * count; - String totalPriceString = String.format(Locale.ENGLISH, "%1$,.1f", totalPrice * motesMultiplier); - - // If count is 1, return a simple message - if (count == 1) { - return Text.literal(totalPriceString.replace(".0", "") + " Motes").formatted(Formatting.DARK_AQUA); - } - - // If count is greater than 1, include the "each" information - String eachPriceString = String.format(Locale.ENGLISH, "%1$,.1f", price * motesMultiplier); - MutableText message = Text.literal(totalPriceString.replace(".0", "") + " Motes ").formatted(Formatting.DARK_AQUA); - message.append(Text.literal("(" + eachPriceString.replace(".0", "") + " each)").formatted(Formatting.GRAY)); - - return message; - } - - // 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 = -1; - - public static void init() { - Scheduler.INSTANCE.scheduleCyclic(() -> { - if (!Utils.isOnSkyblock() && 0 < minute++) { - nullMsgSend = false; - return; - } - - List<CompletableFuture<Void>> futureList = new ArrayList<>(); - if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) { - SkyblockerConfig.Average type = SkyblockerConfigManager.get().general.itemTooltip.avg; - - if (type == SkyblockerConfig.Average.BOTH || oneDayAvgPricesJson == null || threeDayAvgPricesJson == null || minute % 5 == 0) { - futureList.add(CompletableFuture.runAsync(() -> { - oneDayAvgPricesJson = downloadPrices("1 day avg"); - threeDayAvgPricesJson = downloadPrices("3 day avg"); - })); - } else if (type == SkyblockerConfig.Average.ONE_DAY) { - futureList.add(CompletableFuture.runAsync(() -> oneDayAvgPricesJson = downloadPrices("1 day avg"))); - } else if (type == SkyblockerConfig.Average.THREE_DAY) { - futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg"))); - } - } - if (SkyblockerConfigManager.get().general.itemTooltip.enableLowestBIN || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator) - futureList.add(CompletableFuture.runAsync(() -> lowestPricesJson = downloadPrices("lowest bins"))); - - if (SkyblockerConfigManager.get().general.itemTooltip.enableBazaarPrice || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator) - futureList.add(CompletableFuture.runAsync(() -> bazaarPricesJson = downloadPrices("bazaar"))); - - if (SkyblockerConfigManager.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null) - futureList.add(CompletableFuture.runAsync(() -> npcPricesJson = downloadPrices("npc"))); - - if (SkyblockerConfigManager.get().general.itemTooltip.enableMuseumDate && isMuseumJson == null) - futureList.add(CompletableFuture.runAsync(() -> isMuseumJson = downloadPrices("museum"))); - - if (SkyblockerConfigManager.get().general.itemTooltip.enableMotesPrice && motesPricesJson == null) - futureList.add(CompletableFuture.runAsync(() -> motesPricesJson = downloadPrices("motes"))); - - minute++; - CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])) - .whenComplete((unused, throwable) -> nullMsgSend = false); - }, 1200); - } - - private static JsonObject downloadPrices(String type) { - try { - String url = apiAddresses.get(type); - - if (type.equals("npc") || type.equals("museum") || type.equals("motes")) { - HttpHeaders headers = Http.sendHeadRequest(url); - long combinedHash = Http.getEtag(headers).hashCode() + Http.getLastModified(headers).hashCode(); - - switch (type) { - case "npc": if (npcHash == combinedHash) return npcPricesJson; else npcHash = combinedHash; - case "museum": if (museumHash == combinedHash) return isMuseumJson; else museumHash = combinedHash; - case "motes": if (motesHash == combinedHash) return motesPricesJson; else motesHash = combinedHash; - } - } - - String apiResponse = Http.sendGetRequest(url); - - return new Gson().fromJson(apiResponse, JsonObject.class); - } catch (Exception e) { - LOGGER.warn("[Skyblocker] Failed to download " + type + " prices!", e); - return null; - } - } - - public static JsonObject getBazaarPrices() { - return bazaarPricesJson; - } - - public static JsonObject getLBINPrices() { - return lowestPricesJson; - } - - static { - apiAddresses = new HashMap<>(); - apiAddresses.put("1 day avg", "https://moulberry.codes/auction_averages_lbin/1day.json"); - apiAddresses.put("3 day avg", "https://moulberry.codes/auction_averages_lbin/3day.json"); - apiAddresses.put("bazaar", "https://hysky.de/api/bazaar"); - apiAddresses.put("lowest bins", "https://hysky.de/api/auctions/lowestbins"); - apiAddresses.put("npc", "https://hysky.de/api/npcprice"); - apiAddresses.put("museum", "https://hysky.de/api/museum"); - apiAddresses.put("motes", "https://hysky.de/api/motesprice"); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java deleted file mode 100644 index f7ff1fb9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/SkyblockItemRarity.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import net.minecraft.util.Formatting; - -public enum SkyblockItemRarity { - ADMIN(Formatting.DARK_RED), - VERY_SPECIAL(Formatting.RED), - SPECIAL(Formatting.RED), - DIVINE(Formatting.AQUA), - MYTHIC(Formatting.LIGHT_PURPLE), - LEGENDARY(Formatting.GOLD), - EPIC(Formatting.DARK_PURPLE), - RARE(Formatting.BLUE), - UNCOMMON(Formatting.GREEN), - COMMON(Formatting.WHITE); - - public final float r; - public final float g; - public final float b; - - SkyblockItemRarity(Formatting formatting) { - @SuppressWarnings("DataFlowIssue") - int rgb = formatting.getColorValue(); - - this.r = ((rgb >> 16) & 0xFF) / 255f; - this.g = ((rgb >> 8) & 0xFF) / 255f; - this.b = (rgb & 0xFF) / 255f; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java deleted file mode 100644 index a6412cf7..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/item/WikiLookup.java +++ /dev/null @@ -1,56 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.option.KeyBinding; -import net.minecraft.client.util.InputUtil; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.screen.slot.Slot; -import net.minecraft.text.Text; -import net.minecraft.util.Util; -import org.lwjgl.glfw.GLFW; - -import java.util.concurrent.CompletableFuture; - -public class WikiLookup { - public static KeyBinding wikiLookup; - static final MinecraftClient client = MinecraftClient.getInstance(); - static String id; - - public static void init() { - wikiLookup = KeyBindingHelper.registerKeyBinding(new KeyBinding( - "key.wikiLookup", - InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_F4, - "key.categories.skyblocker" - )); - } - - public static String getSkyblockId(Slot slot) { - //Grabbing the skyblock NBT data - ItemStack selectedStack = slot.getStack(); - NbtCompound nbt = selectedStack.getSubNbt("ExtraAttributes"); - if (nbt != null) { - id = nbt.getString("id"); - } - return id; - } - - public static void openWiki(Slot slot) { - if (Utils.isOnSkyblock()) { - id = getSkyblockId(slot); - try { - String wikiLink = ItemRegistry.getWikiLink(id); - CompletableFuture.runAsync(() -> Util.getOperatingSystem().open(wikiLink)); - } catch (IndexOutOfBoundsException | IllegalStateException e) { - e.printStackTrace(); - if (client.player != null) - client.player.sendMessage(Text.of("Error while retrieving wiki article..."), false); - } - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemFixerUpper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemFixerUpper.java deleted file mode 100644 index cc7b216c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemFixerUpper.java +++ /dev/null @@ -1,341 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import java.util.Map; - -public class ItemFixerUpper { - private final static String[] ANVIL_VARIANTS = { - "minecraft:anvil", - "minecraft:chipped_anvil", - "minecraft:damaged_anvil" - }; - - private final static String[] COAL_VARIANTS = { - "minecraft:coal", - "minecraft:charcoal" - }; - - private final static String[] COBBLESTONE_WALL_VARIANTS = { - "minecraft:cobblestone_wall", - "minecraft:mossy_cobblestone_wall" - }; - - private final static String[] COOKED_FISH_VARIANTS = { - "minecraft:cooked_cod", - "minecraft:cooked_salmon" - }; - - private final static String[] DIRT_VARIANTS = { - "minecraft:dirt", - "minecraft:coarse_dirt", - "minecraft:podzol" - }; - - private final static String[] DOUBLE_PLANT_VARIANTS = { - "minecraft:sunflower", - "minecraft:lilac", - "minecraft:tall_grass", - "minecraft:large_fern", - "minecraft:rose_bush", - "minecraft:peony" - }; - - private final static String[] DYE_VARIANTS = { - "minecraft:ink_sac", - "minecraft:red_dye", - "minecraft:green_dye", - "minecraft:cocoa_beans", - "minecraft:lapis_lazuli", - "minecraft:purple_dye", - "minecraft:cyan_dye", - "minecraft:light_gray_dye", - "minecraft:gray_dye", - "minecraft:pink_dye", - "minecraft:lime_dye", - "minecraft:yellow_dye", - "minecraft:light_blue_dye", - "minecraft:magenta_dye", - "minecraft:orange_dye", - "minecraft:bone_meal" - }; - - private final static String[] FISH_VARIANTS = { - "minecraft:cod", - "minecraft:salmon", - "minecraft:tropical_fish", - "minecraft:pufferfish" - }; - - private final static String[] GOLDEN_APPLE_VARIANTS = { - "minecraft:golden_apple", - "minecraft:enchanted_golden_apple" - }; - - private final static String[] LOG_VARIANTS = { - "minecraft:oak_log", - "minecraft:spruce_log", - "minecraft:birch_log", - "minecraft:jungle_log", - "minecraft:oak_wood", - "minecraft:spruce_wood", - "minecraft:birch_wood", - "minecraft:jungle_wood", - }; - - private final static String[] LOG2_VARIANTS = { - "minecraft:acacia_log", - "minecraft:dark_oak_log", - "minecraft:acacia_wood", - "minecraft:dark_oak_wood" - }; - - private final static String[] MONSTER_EGG_VARIANTS = { - "minecraft:infested_stone", - "minecraft:infested_cobblestone", - "minecraft:infested_stone_bricks", - "minecraft:infested_mossy_stone_bricks", - "minecraft:infested_cracked_stone_bricks", - "minecraft:infested_chiseled_stone_bricks" - }; - - private final static String[] PRISMARINE_VARIANTS = { - "minecraft:prismarine", - "minecraft:prismarine_bricks", - "minecraft:dark_prismarine" - }; - - private final static String[] QUARTZ_BLOCK_VARIANTS = { - "minecraft:quartz_block", - "minecraft:chiseled_quartz_block", - "minecraft:quartz_pillar" - }; - - private final static String[] RED_FLOWER_VARIANTS = { - "minecraft:poppy", - "minecraft:blue_orchid", - "minecraft:allium", - "minecraft:azure_bluet", - "minecraft:red_tulip", - "minecraft:orange_tulip", - "minecraft:white_tulip", - "minecraft:pink_tulip", - "minecraft:oxeye_daisy" - }; - - private final static String[] SAND_VARIANTS = { - "minecraft:sand", - "minecraft:red_sand" - }; - - private final static String[] SKULL_VARIANTS = { - "minecraft:skeleton_skull", - "minecraft:wither_skeleton_skull", - "minecraft:zombie_head", - "minecraft:player_head", - "minecraft:creeper_head" - }; - - private final static String[] SPONGE_VARIANTS = { - "minecraft:sponge", - "minecraft:wet_sponge" - }; - - private final static String[] STONE_VARIANTS = { - "minecraft:stone", - "minecraft:granite", - "minecraft:polished_granite", - "minecraft:diorite", - "minecraft:polished_diorite", - "minecraft:andesite", - "minecraft:polished_andesite" - }; - - private final static String[] STONE_SLAB_VARIANTS = { - "minecraft:smooth_stone_slab", - "minecraft:sandstone_slab", - "minecraft:petrified_oak_slab", - "minecraft:cobblestone_slab", - "minecraft:brick_slab", - "minecraft:stone_brick_slab", - "minecraft:nether_brick_slab", - "minecraft:quartz_slab" - }; - - private final static String[] STONEBRICK_VARIANTS = { - "minecraft:stone_bricks", - "minecraft:mossy_stone_bricks", - "minecraft:cracked_stone_bricks", - "minecraft:chiseled_stone_bricks" - }; - - private final static String[] TALLGRASS_VARIANTS = { - "minecraft:dead_bush", - "minecraft:grass", - "minecraft:fern" - }; - - private final static Map<Integer, String> SPAWN_EGG_VARIANTS = Map.ofEntries( - //This entry 0 is technically not right but Hypixel decided to make it polar bear so well we use that - Map.entry(0, "minecraft:polar_bear_spawn_egg"), - Map.entry(50, "minecraft:creeper_spawn_egg"), - Map.entry(51, "minecraft:skeleton_spawn_egg"), - Map.entry(52, "minecraft:spider_spawn_egg"), - Map.entry(54, "minecraft:zombie_spawn_egg"), - Map.entry(55, "minecraft:slime_spawn_egg"), - Map.entry(56, "minecraft:ghast_spawn_egg"), - Map.entry(57, "minecraft:zombified_piglin_spawn_egg"), - Map.entry(58, "minecraft:enderman_spawn_egg"), - Map.entry(59, "minecraft:cave_spider_spawn_egg"), - Map.entry(60, "minecraft:silverfish_spawn_egg"), - Map.entry(61, "minecraft:blaze_spawn_egg"), - Map.entry(62, "minecraft:magma_cube_spawn_egg"), - Map.entry(65, "minecraft:bat_spawn_egg"), - Map.entry(66, "minecraft:witch_spawn_egg"), - Map.entry(67, "minecraft:endermite_spawn_egg"), - Map.entry(68, "minecraft:guardian_spawn_egg"), - Map.entry(90, "minecraft:pig_spawn_egg"), - Map.entry(91, "minecraft:sheep_spawn_egg"), - Map.entry(92, "minecraft:cow_spawn_egg"), - Map.entry(93, "minecraft:chicken_spawn_egg"), - Map.entry(94, "minecraft:squid_spawn_egg"), - Map.entry(95, "minecraft:wolf_spawn_egg"), - Map.entry(96, "minecraft:mooshroom_spawn_egg"), - Map.entry(98, "minecraft:ocelot_spawn_egg"), - Map.entry(100, "minecraft:horse_spawn_egg"), - Map.entry(101, "minecraft:rabbit_spawn_egg"), - Map.entry(120, "minecraft:villager_spawn_egg") - ); - - private final static String[] SANDSTONE_VARIANTS = { - ":", - ":chiseled_", - ":cut_" - }; - - private final static String[] COLOR_VARIANTS = { - ":white_", - ":orange_", - ":magenta_", - ":light_blue_", - ":yellow_", - ":lime_", - ":pink_", - ":gray_", - ":light_gray_", - ":cyan_", - ":purple_", - ":blue_", - ":brown_", - ":green_", - ":red_", - ":black_" - }; - - private final static String[] WOOD_VARIANTS = { - ":oak_", - ":spruce_", - ":birch_", - ":jungle_", - ":acacia_", - ":dark_oak_" - }; - - //this is the map of all renames - private final static Map<String, String> RENAMED = Map.ofEntries( - Map.entry("minecraft:bed", "minecraft:red_bed"), - Map.entry("minecraft:boat", "minecraft:oak_boat"), - Map.entry("minecraft:brick_block", "minecraft:bricks"), - Map.entry("minecraft:deadbush", "minecraft:dead_bush"), - Map.entry("minecraft:fence_gate", "minecraft:oak_fence_gate"), - Map.entry("minecraft:fence", "minecraft:oak_fence"), - Map.entry("minecraft:firework_charge", "minecraft:firework_star"), - Map.entry("minecraft:fireworks", "minecraft:firework_rocket"), - Map.entry("minecraft:golden_rail", "minecraft:powered_rail"), - Map.entry("minecraft:grass", "minecraft:grass_block"), - Map.entry("minecraft:hardened_clay", "minecraft:terracotta"), - Map.entry("minecraft:lit_pumpkin", "minecraft:jack_o_lantern"), - Map.entry("minecraft:melon_block", "minecraft:melon"), - Map.entry("minecraft:melon", "minecraft:melon_slice"), - Map.entry("minecraft:mob_spawner", "minecraft:spawner"), - Map.entry("minecraft:nether_brick", "minecraft:nether_bricks"), - Map.entry("minecraft:netherbrick", "minecraft:nether_brick"), - Map.entry("minecraft:noteblock", "minecraft:note_block"), - Map.entry("minecraft:piston_extension", "minecraft:moving_piston"), - Map.entry("minecraft:portal", "minecraft:nether_portal"), - Map.entry("minecraft:pumpkin", "minecraft:carved_pumpkin"), - Map.entry("minecraft:quartz_ore", "minecraft:nether_quartz_ore"), - Map.entry("minecraft:record_11", "minecraft:music_disc_11"), - Map.entry("minecraft:record_13", "minecraft:music_disc_13"), - Map.entry("minecraft:record_blocks", "minecraft:music_disc_blocks"), - Map.entry("minecraft:record_cat", "minecraft:music_disc_cat"), - Map.entry("minecraft:record_chirp", "minecraft:music_disc_chirp"), - Map.entry("minecraft:record_far", "minecraft:music_disc_far"), - Map.entry("minecraft:record_mall", "minecraft:music_disc_mall"), - Map.entry("minecraft:record_mellohi", "minecraft:music_disc_mellohi"), - Map.entry("minecraft:record_stal", "minecraft:music_disc_stal"), - Map.entry("minecraft:record_strad", "minecraft:music_disc_strad"), - Map.entry("minecraft:record_wait", "minecraft:music_disc_wait"), - Map.entry("minecraft:record_ward", "minecraft:music_disc_ward"), - Map.entry("minecraft:red_nether_brick", "minecraft:red_nether_bricks"), - Map.entry("minecraft:reeds", "minecraft:sugar_cane"), - Map.entry("minecraft:sign", "minecraft:oak_sign"), - Map.entry("minecraft:slime", "minecraft:slime_block"), - Map.entry("minecraft:snow_layer", "minecraft:snow"), - Map.entry("minecraft:snow", "minecraft:snow_block"), - Map.entry("minecraft:speckled_melon", "minecraft:glistering_melon_slice"), - Map.entry("minecraft:stone_slab2", "minecraft:red_sandstone_slab"), - Map.entry("minecraft:stone_stairs", "minecraft:cobblestone_stairs"), - Map.entry("minecraft:trapdoor", "minecraft:oak_trapdoor"), - Map.entry("minecraft:waterlily", "minecraft:lily_pad"), - Map.entry("minecraft:web", "minecraft:cobweb"), - Map.entry("minecraft:wooden_button", "minecraft:oak_button"), - Map.entry("minecraft:wooden_door", "minecraft:oak_door"), - Map.entry("minecraft:wooden_pressure_plate", "minecraft:oak_pressure_plate"), - Map.entry("minecraft:yellow_flower", "minecraft:dandelion") - ); - - //TODO : Add mushroom block variants - //i'll do it later because it isn't used and unlike the other, it's not just a rename or a separate, it's a separate and a merge - - public static String convertItemId(String id, int damage) { - return switch (id) { - //all the case are simple separate - case "minecraft:anvil" -> ANVIL_VARIANTS[damage]; - case "minecraft:coal" -> COAL_VARIANTS[damage]; - case "minecraft:cobblestone_wall" -> COBBLESTONE_WALL_VARIANTS[damage]; - case "minecraft:cooked_fish" -> COOKED_FISH_VARIANTS[damage]; - case "minecraft:dirt" -> DIRT_VARIANTS[damage]; - case "minecraft:double_plant" -> DOUBLE_PLANT_VARIANTS[damage]; - case "minecraft:dye" -> DYE_VARIANTS[damage]; - case "minecraft:fish" -> FISH_VARIANTS[damage]; - case "minecraft:golden_apple" -> GOLDEN_APPLE_VARIANTS[damage]; - case "minecraft:log" -> LOG_VARIANTS[damage]; - case "minecraft:log2" -> LOG2_VARIANTS[damage]; - case "minecraft:monster_egg" -> MONSTER_EGG_VARIANTS[damage]; - case "minecraft:prismarine" -> PRISMARINE_VARIANTS[damage]; - case "minecraft:quartz_block" -> QUARTZ_BLOCK_VARIANTS[damage]; - case "minecraft:red_flower" -> RED_FLOWER_VARIANTS[damage]; - case "minecraft:sand" -> SAND_VARIANTS[damage]; - case "minecraft:skull" -> SKULL_VARIANTS[damage]; - case "minecraft:sponge" -> SPONGE_VARIANTS[damage]; - case "minecraft:stone" -> STONE_VARIANTS[damage]; - case "minecraft:stone_slab" -> STONE_SLAB_VARIANTS[damage]; - case "minecraft:stonebrick" -> STONEBRICK_VARIANTS[damage]; - case "minecraft:tallgrass" -> TALLGRASS_VARIANTS[damage]; - //we use a Map from int to str instead of an array because numbers are not consecutive - case "minecraft:spawn_egg" -> SPAWN_EGG_VARIANTS.get(damage); - //when we use the generalized variant we need to replaceFirst - case "minecraft:sandstone", "minecraft:red_sandstone" -> id.replaceFirst(":", SANDSTONE_VARIANTS[damage]); - //to use the general color variants we need to reverse the order because Minecraft decided so for some reason - case "minecraft:banner" -> id.replaceFirst(":", COLOR_VARIANTS[15 - damage]); - case "minecraft:carpet", "minecraft:stained_glass", "minecraft:stained_glass_pane", "minecraft:wool" -> id.replaceFirst(":", COLOR_VARIANTS[damage]); - //for the terracotta we replace the whole name by the color and append "terracotta" at the end - case "minecraft:stained_hardened_clay" -> id.replaceFirst(":stained_hardened_clay", COLOR_VARIANTS[damage]) + "terracotta"; - //for the wooden slab we need to remove the "wooden_" prefix, but otherwise it's the same, so I just combined them anyway - case "minecraft:leaves", "minecraft:planks", "minecraft:sapling", "minecraft:wooden_slab" -> id.replaceFirst(":(?:wooden_)?", WOOD_VARIANTS[damage]); - //here we replace the 2 by nothing to remove it as it's not needed anymore - case "minecraft:leaves2" -> id.replaceFirst(":", WOOD_VARIANTS[damage + 4]).replaceFirst("2", ""); - //the default case is just a rename or no change - default -> RENAMED.getOrDefault(id, id); - }; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java deleted file mode 100644 index 4fc24e6c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemListWidget.java +++ /dev/null @@ -1,102 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import com.mojang.blaze3d.systems.RenderSystem; -import me.xmrvizzy.skyblocker.mixin.accessor.RecipeBookWidgetAccessor; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.screen.AbstractRecipeScreenHandler; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -@Environment(value = EnvType.CLIENT) -public class ItemListWidget extends RecipeBookWidget { - private int parentWidth; - private int parentHeight; - private int leftOffset; - private TextFieldWidget searchField; - private SearchResultsWidget results; - - public ItemListWidget() { - super(); - } - - public void updateSearchResult() { - this.results.updateSearchResult(((RecipeBookWidgetAccessor) this).getSearchText()); - } - - @Override - public void initialize(int parentWidth, int parentHeight, MinecraftClient client, boolean narrow, AbstractRecipeScreenHandler<?> craftingScreenHandler) { - super.initialize(parentWidth, parentHeight, client, narrow, craftingScreenHandler); - this.parentWidth = parentWidth; - this.parentHeight = parentHeight; - this.leftOffset = narrow ? 0 : 86; - this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); - int x = (this.parentWidth - 147) / 2 - this.leftOffset; - int y = (this.parentHeight - 166) / 2; - if (ItemRegistry.filesImported) { - this.results = new SearchResultsWidget(this.client, x, y); - this.updateSearchResult(); - } - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - if (this.isOpen()) { - MatrixStack matrices = context.getMatrices(); - matrices.push(); - matrices.translate(0.0D, 0.0D, 100.0D); - RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); - this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); - int i = (this.parentWidth - 147) / 2 - this.leftOffset; - int j = (this.parentHeight - 166) / 2; - context.drawTexture(TEXTURE, i, j, 1, 1, 147, 166); - this.searchField = ((RecipeBookWidgetAccessor) this).getSearchField(); - - if (!ItemRegistry.filesImported && !this.searchField.isFocused() && this.searchField.getText().isEmpty()) { - Text hintText = (Text.literal("Loading...")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); - context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1); - } else if (!this.searchField.isFocused() && this.searchField.getText().isEmpty()) { - Text hintText = (Text.translatable("gui.recipebook.search_hint")).formatted(Formatting.ITALIC).formatted(Formatting.GRAY); - context.drawTextWithShadow(this.client.textRenderer, hintText, i + 25, j + 14, -1); - } else { - this.searchField.render(context, mouseX, mouseY, delta); - } - if (ItemRegistry.filesImported) { - if (results == null) { - int x = (this.parentWidth - 147) / 2 - this.leftOffset; - int y = (this.parentHeight - 166) / 2; - this.results = new SearchResultsWidget(this.client, x, y); - } - this.updateSearchResult(); - this.results.render(context, mouseX, mouseY, delta); - } - matrices.pop(); - } - } - - @Override - public void drawTooltip(DrawContext context, int x, int y, int mouseX, int mouseY) { - if (this.isOpen() && ItemRegistry.filesImported && results != null) { - this.results.drawTooltip(context, mouseX, mouseY); - } - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (this.isOpen() && this.client.player != null && !this.client.player.isSpectator() && ItemRegistry.filesImported && this.searchField != null && results != null) { - if (this.searchField.mouseClicked(mouseX, mouseY, button)) { - this.results.closeRecipeView(); - this.searchField.setFocused(true); - return true; - } else { - this.searchField.setFocused(false); - return this.results.mouseClicked(mouseX, mouseY, button); - } - } else return false; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java deleted file mode 100644 index 5eb9e488..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemRegistry.java +++ /dev/null @@ -1,137 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.xmrvizzy.skyblocker.utils.NEURepo; -import net.minecraft.client.MinecraftClient; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.text.Text; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Stream; - -public class ItemRegistry { - protected static final Logger LOGGER = LoggerFactory.getLogger(ItemRegistry.class); - protected static final Path ITEM_LIST_DIR = NEURepo.LOCAL_REPO_DIR.resolve("items"); - - protected static final List<ItemStack> items = new ArrayList<>(); - protected static final Map<String, ItemStack> itemsMap = new HashMap<>(); - protected static final List<SkyblockCraftingRecipe> recipes = new ArrayList<>(); - public static final MinecraftClient client = MinecraftClient.getInstance(); - public static boolean filesImported = false; - - public static void init() { - NEURepo.runAsyncAfterLoad(ItemStackBuilder::loadPetNums); - NEURepo.runAsyncAfterLoad(ItemRegistry::importItemFiles); - } - - private static void importItemFiles() { - List<JsonObject> jsonObjs = new ArrayList<>(); - - File dir = ITEM_LIST_DIR.toFile(); - File[] files = dir.listFiles(); - if (files == null) { - return; - } - for (File file : files) { - Path path = ITEM_LIST_DIR.resolve(file.getName()); - try { - String fileContent = Files.readString(path); - jsonObjs.add(JsonParser.parseString(fileContent).getAsJsonObject()); - } catch (Exception e) { - LOGGER.error("Failed to read file " + path, e); - } - } - - for (JsonObject jsonObj : jsonObjs) { - String internalName = jsonObj.get("internalname").getAsString(); - ItemStack itemStack = ItemStackBuilder.parseJsonObj(jsonObj); - items.add(itemStack); - itemsMap.put(internalName, itemStack); - } - for (JsonObject jsonObj : jsonObjs) - if (jsonObj.has("recipe")) { - recipes.add(SkyblockCraftingRecipe.fromJsonObject(jsonObj)); - } - - items.sort((lhs, rhs) -> { - String lhsInternalName = getInternalName(lhs); - String lhsFamilyName = lhsInternalName.replaceAll(".\\d+$", ""); - String rhsInternalName = getInternalName(rhs); - String rhsFamilyName = rhsInternalName.replaceAll(".\\d+$", ""); - if (lhsFamilyName.equals(rhsFamilyName)) { - if (lhsInternalName.length() != rhsInternalName.length()) - return lhsInternalName.length() - rhsInternalName.length(); - else return lhsInternalName.compareTo(rhsInternalName); - } - return lhsFamilyName.compareTo(rhsFamilyName); - }); - filesImported = true; - } - - public static String getWikiLink(String internalName) { - try { - String fileContent = Files.readString(ITEM_LIST_DIR.resolve(internalName + ".json")); - JsonObject fileJson = JsonParser.parseString(fileContent).getAsJsonObject(); - //TODO optional official or unofficial wiki link - try { - return fileJson.get("info").getAsJsonArray().get(1).getAsString(); - } catch (IndexOutOfBoundsException e) { - return fileJson.get("info").getAsJsonArray().get(0).getAsString(); - } - } catch (IOException | NullPointerException e) { - LOGGER.error("Failed to read item file " + internalName + ".json", e); - if (client.player != null) { - client.player.sendMessage(Text.of("Can't locate a wiki article for this item..."), false); - } - return null; - } - } - - public static List<SkyblockCraftingRecipe> getRecipes(String internalName) { - List<SkyblockCraftingRecipe> result = new ArrayList<>(); - for (SkyblockCraftingRecipe recipe : recipes) - if (getInternalName(recipe.result).equals(internalName)) result.add(recipe); - for (SkyblockCraftingRecipe recipe : recipes) - for (ItemStack ingredient : recipe.grid) - if (!ingredient.getItem().equals(Items.AIR) && getInternalName(ingredient).equals(internalName)) { - result.add(recipe); - break; - } - return result; - } - - public static Stream<SkyblockCraftingRecipe> getRecipesStream() { - return recipes.stream(); - } - - public static Stream<ItemStack> getItemsStream() { - return items.stream(); - } - - /** - * Get Internal name of an ItemStack - * - * @param itemStack ItemStack to get internal name from - * @return internal name of the given ItemStack - */ - public static String getInternalName(ItemStack itemStack) { - if (itemStack.getNbt() == null) return ""; - return itemStack.getNbt().getCompound("ExtraAttributes").getString("id"); - } - - public static ItemStack getItemStack(String internalName) { - return itemsMap.get(internalName); - } -} - diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java deleted file mode 100644 index 8028099a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ItemStackBuilder.java +++ /dev/null @@ -1,154 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.xmrvizzy.skyblocker.utils.NEURepo; -import net.minecraft.item.FireworkRocketItem; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.*; -import net.minecraft.text.Text; -import net.minecraft.util.Pair; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class ItemStackBuilder { - private final static Path PETNUMS_PATH = NEURepo.LOCAL_REPO_DIR.resolve("constants/petnums.json"); - private static JsonObject petNums; - - public static void loadPetNums() { - try { - petNums = JsonParser.parseString(Files.readString(PETNUMS_PATH)).getAsJsonObject(); - } catch (Exception e) { - ItemRegistry.LOGGER.error("Failed to load petnums.json"); - } - } - - public static ItemStack parseJsonObj(JsonObject obj) { - String internalName = obj.get("internalname").getAsString(); - - List<Pair<String, String>> injectors = new ArrayList<>(petData(internalName)); - - NbtCompound root = new NbtCompound(); - root.put("Count", NbtByte.of((byte)1)); - - String id = obj.get("itemid").getAsString(); - int damage = obj.get("damage").getAsInt(); - root.put("id", NbtString.of(ItemFixerUpper.convertItemId(id, damage))); - - NbtCompound tag = new NbtCompound(); - root.put("tag", tag); - - NbtCompound extra = new NbtCompound(); - tag.put("ExtraAttributes", extra); - extra.put("id", NbtString.of(internalName)); - - NbtCompound display = new NbtCompound(); - tag.put("display", display); - - String name = injectData(obj.get("displayname").getAsString(), injectors); - display.put("Name", NbtString.of(Text.Serializer.toJson(Text.of(name)))); - - NbtList lore = new NbtList(); - display.put("Lore", lore); - obj.get("lore").getAsJsonArray().forEach(el -> - lore.add(NbtString.of(Text.Serializer.toJson(Text.of(injectData(el.getAsString(), injectors))))) - ); - - String nbttag = obj.get("nbttag").getAsString(); - // add skull texture - Matcher skullUuid = Pattern.compile("(?<=SkullOwner:\\{)Id:\"(.{36})\"").matcher(nbttag); - Matcher skullTexture = Pattern.compile("(?<=Properties:\\{textures:\\[0:\\{Value:)\"(.+?)\"").matcher(nbttag); - if (skullUuid.find() && skullTexture.find()) { - NbtCompound skullOwner = new NbtCompound(); - tag.put("SkullOwner", skullOwner); - UUID uuid = UUID.fromString(skullUuid.group(1)); - skullOwner.put("Id", NbtHelper.fromUuid(uuid)); - skullOwner.put("Name", NbtString.of(internalName)); - - NbtCompound properties = new NbtCompound(); - skullOwner.put("Properties", properties); - NbtList textures = new NbtList(); - properties.put("textures", textures); - NbtCompound texture = new NbtCompound(); - textures.add(texture); - texture.put("Value", NbtString.of(skullTexture.group(1))); - } - // add leather armor dye color - Matcher colorMatcher = Pattern.compile("color:(\\d+)").matcher(nbttag); - if (colorMatcher.find()) { - NbtInt color = NbtInt.of(Integer.parseInt(colorMatcher.group(1))); - display.put("color", color); - } - // add enchantment glint - if (nbttag.contains("ench:")) { - NbtList enchantments = new NbtList(); - enchantments.add(new NbtCompound()); - tag.put("Enchantments", enchantments); - } - - // Add firework star color - Matcher explosionColorMatcher = Pattern.compile("\\{Explosion:\\{(?:Type:[0-9a-z]+,)?Colors:\\[(?<color>[0-9]+)\\]\\}").matcher(nbttag); - if (explosionColorMatcher.find()) { - NbtCompound explosion = new NbtCompound(); - - explosion.putInt("Type", FireworkRocketItem.Type.SMALL_BALL.getId()); //Forget about the actual ball type because it probably doesn't matter - explosion.putIntArray("Colors", new int[] { Integer.parseInt(explosionColorMatcher.group("color")) }); - tag.put("Explosion", explosion); - } - - return ItemStack.fromNbt(root); - } - - // TODO: fix stats for GOLDEN_DRAGON (lv1 -> lv200) - private static List<Pair<String, String>> petData(String internalName) { - List<Pair<String, String>> list = new ArrayList<>(); - - String petName = internalName.split(";")[0]; - if (!internalName.contains(";") || !petNums.has(petName)) return list; - - list.add(new Pair<>("\\{LVL\\}", "1 ➡ 100")); - - final String[] rarities = { - "COMMON", - "UNCOMMON", - "RARE", - "EPIC", - "LEGENDARY", - "MYTHIC" - }; - String rarity = rarities[Integer.parseInt(internalName.split(";")[1])]; - JsonObject data = petNums.get(petName).getAsJsonObject().get(rarity).getAsJsonObject(); - - JsonObject statNumsMin = data.get("1").getAsJsonObject().get("statNums").getAsJsonObject(); - JsonObject statNumsMax = data.get("100").getAsJsonObject().get("statNums").getAsJsonObject(); - Set<Map.Entry<String, JsonElement>> entrySet = statNumsMin.entrySet(); - for (Map.Entry<String, JsonElement> entry : entrySet) { - String key = entry.getKey(); - String left = "\\{" + key+ "\\}"; - String right = statNumsMin.get(key).getAsString() + " ➡ " + statNumsMax.get(key).getAsString(); - list.add(new Pair<>(left, right)); - } - - JsonArray otherNumsMin = data.get("1").getAsJsonObject().get("otherNums").getAsJsonArray(); - JsonArray otherNumsMax = data.get("100").getAsJsonObject().get("otherNums").getAsJsonArray(); - for (int i = 0; i < otherNumsMin.size(); ++i) { - String left = "\\{" + i + "\\}"; - String right = otherNumsMin.get(i).getAsString() + " ➡ " + otherNumsMax.get(i).getAsString(); - list.add(new Pair<>(left, right)); - } - - return list; - } - - private static String injectData(String string, List<Pair<String, String>> injectors) { - for (Pair<String, String> injector : injectors) - string = string.replaceAll(injector.getLeft(), injector.getRight()); - return string; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java deleted file mode 100644 index 61a9aa20..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/ResultButtonWidget.java +++ /dev/null @@ -1,65 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import java.util.List; -import java.util.ArrayList; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.text.OrderedText; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -public class ResultButtonWidget extends ClickableWidget { - private static final Identifier BACKGROUND_TEXTURE = new Identifier("recipe_book/slot_craftable"); - - protected ItemStack itemStack = null; - - public ResultButtonWidget(int x, int y) { - super(x, y, 25, 25, Text.of("")); - } - - protected void setItemStack(ItemStack itemStack) { - this.active = !itemStack.getItem().equals(Items.AIR); - this.visible = true; - this.itemStack = itemStack; - } - - protected void clearItemStack() { - this.visible = false; - this.itemStack = null; - } - - @Override - public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) { - MinecraftClient client = MinecraftClient.getInstance(); - // this.drawTexture(matrices, this.x, this.y, 29, 206, this.width, this.height); - context.drawGuiTexture(BACKGROUND_TEXTURE, this.getX(), this.getY(), this.getWidth(), this.getHeight()); - // client.getItemRenderer().renderInGui(this.itemStack, this.x + 4, this.y + 4); - context.drawItem(this.itemStack, this.getX() + 4, this.getY() + 4); - // client.getItemRenderer().renderGuiItemOverlay(client.textRenderer, itemStack, this.x + 4, this.y + 4); - context.drawItemInSlot(client.textRenderer, itemStack, this.getX() + 4, this.getY() + 4); - } - - public void renderTooltip(DrawContext context, int mouseX, int mouseY) { - MinecraftClient client = MinecraftClient.getInstance(); - List<Text> tooltip = Screen.getTooltipFromItem(client, this.itemStack); - List<OrderedText> orderedTooltip = new ArrayList<>(); - - for(int i = 0; i < tooltip.size(); i++) { - orderedTooltip.add(tooltip.get(i).asOrderedText()); - } - - client.currentScreen.setTooltip(orderedTooltip); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - // TODO Auto-generated method stub - - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java deleted file mode 100644 index 38060584..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SearchResultsWidget.java +++ /dev/null @@ -1,228 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Drawable; -import net.minecraft.client.gui.screen.ButtonTextures; -import net.minecraft.client.gui.widget.ToggleButtonWidget; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.jetbrains.annotations.Nullable; - -public class SearchResultsWidget implements Drawable { - private static final ButtonTextures PAGE_FORWARD_TEXTURES = new ButtonTextures(new Identifier("recipe_book/page_forward"), new Identifier("recipe_book/page_forward_highlighted")); - private static final ButtonTextures PAGE_BACKWARD_TEXTURES = new ButtonTextures(new Identifier("recipe_book/page_backward"), new Identifier("recipe_book/page_backward_highlighted")); - private static final int COLS = 5; - private static final int MAX_TEXT_WIDTH = 124; - private static final String ELLIPSIS = "..."; - private static final Pattern FORMATTING_CODE_PATTERN = Pattern.compile("(?i)§[0-9A-FK-OR]"); - - private final MinecraftClient client; - private final int parentX; - private final int parentY; - - private final List<ItemStack> searchResults = new ArrayList<>(); - private List<SkyblockCraftingRecipe> recipeResults = new ArrayList<>(); - private String searchText = null; - private final List<ResultButtonWidget> resultButtons = new ArrayList<>(); - private final ToggleButtonWidget nextPageButton; - private final ToggleButtonWidget prevPageButton; - private int currentPage = 0; - private int pageCount = 0; - private boolean displayRecipes = false; - - public SearchResultsWidget(MinecraftClient client, int parentX, int parentY) { - this.client = client; - this.parentX = parentX; - this.parentY = parentY; - int gridX = parentX + 11; - int gridY = parentY + 31; - int rows = 4; - for (int i = 0; i < rows; ++i) - for (int j = 0; j < COLS; ++j) { - int x = gridX + j * 25; - int y = gridY + i * 25; - resultButtons.add(new ResultButtonWidget(x, y)); - } - this.nextPageButton = new ToggleButtonWidget(parentX + 93, parentY + 137, 12, 17, false); - this.nextPageButton.setTextures(PAGE_FORWARD_TEXTURES); - this.prevPageButton = new ToggleButtonWidget(parentX + 38, parentY + 137, 12, 17, true); - this.prevPageButton.setTextures(PAGE_BACKWARD_TEXTURES); - } - - public void closeRecipeView() { - this.currentPage = 0; - this.pageCount = (this.searchResults.size() - 1) / resultButtons.size() + 1; - this.displayRecipes = false; - this.updateButtons(); - } - - protected void updateSearchResult(String searchText) { - if (!searchText.equals(this.searchText)) { - this.searchText = searchText; - this.searchResults.clear(); - for (ItemStack entry : ItemRegistry.items) { - String name = entry.getName().toString().toLowerCase(Locale.ENGLISH); - if (entry.getNbt() == null) { - continue; - } - String disp = entry.getNbt().getCompound("display").toString().toLowerCase(Locale.ENGLISH); - if (name.contains(this.searchText) || disp.contains(this.searchText)) - this.searchResults.add(entry); - } - this.currentPage = 0; - this.pageCount = (this.searchResults.size() - 1) / resultButtons.size() + 1; - this.displayRecipes = false; - this.updateButtons(); - } - } - - private void updateButtons() { - if (this.displayRecipes) { - SkyblockCraftingRecipe recipe = this.recipeResults.get(this.currentPage); - for (ResultButtonWidget button : resultButtons) - button.clearItemStack(); - resultButtons.get(5).setItemStack(recipe.grid.get(0)); - resultButtons.get(6).setItemStack(recipe.grid.get(1)); - resultButtons.get(7).setItemStack(recipe.grid.get(2)); - resultButtons.get(10).setItemStack(recipe.grid.get(3)); - resultButtons.get(11).setItemStack(recipe.grid.get(4)); - resultButtons.get(12).setItemStack(recipe.grid.get(5)); - resultButtons.get(15).setItemStack(recipe.grid.get(6)); - resultButtons.get(16).setItemStack(recipe.grid.get(7)); - resultButtons.get(17).setItemStack(recipe.grid.get(8)); - resultButtons.get(14).setItemStack(recipe.result); - } else { - for (int i = 0; i < resultButtons.size(); ++i) { - int index = this.currentPage * resultButtons.size() + i; - if (index < this.searchResults.size()) { - resultButtons.get(i).setItemStack(this.searchResults.get(index)); - } else { - resultButtons.get(i).clearItemStack(); - } - } - } - this.prevPageButton.active = this.currentPage > 0; - this.nextPageButton.active = this.currentPage < this.pageCount - 1; - } - - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; - RenderSystem.disableDepthTest(); - if (this.displayRecipes) { - //Craft text - usually a requirement for the recipe - String craftText = this.recipeResults.get(this.currentPage).craftText; - if (textRenderer.getWidth(craftText) > MAX_TEXT_WIDTH) { - drawTooltip(textRenderer, context, craftText, this.parentX + 11, this.parentY + 31, mouseX, mouseY); - craftText = textRenderer.trimToWidth(craftText, MAX_TEXT_WIDTH) + ELLIPSIS; - } - context.drawTextWithShadow(textRenderer, craftText, this.parentX + 11, this.parentY + 31, 0xffffffff); - - //Item name - Text resultText = this.recipeResults.get(this.currentPage).result.getName(); - if (textRenderer.getWidth(Formatting.strip(resultText.getString())) > MAX_TEXT_WIDTH) { - drawTooltip(textRenderer, context, resultText, this.parentX + 11, this.parentY + 43, mouseX, mouseY); - resultText = Text.literal(getLegacyFormatting(resultText.getString()) + textRenderer.trimToWidth(Formatting.strip(resultText.getString()), MAX_TEXT_WIDTH) + ELLIPSIS).setStyle(resultText.getStyle()); - } - context.drawTextWithShadow(textRenderer, resultText, this.parentX + 11, this.parentY + 43, 0xffffffff); - - //Arrow pointing to result item from the recipe - context.drawTextWithShadow(textRenderer, "▶", this.parentX + 96, this.parentY + 90, 0xaaffffff); - } - for (ResultButtonWidget button : resultButtons) - button.render(context, mouseX, mouseY, delta); - if (this.pageCount > 1) { - String string = (this.currentPage + 1) + "/" + this.pageCount; - int dx = this.client.textRenderer.getWidth(string) / 2; - context.drawText(textRenderer, string, this.parentX - dx + 73, this.parentY + 141, -1, false); - } - if (this.prevPageButton.active) this.prevPageButton.render(context, mouseX, mouseY, delta); - if (this.nextPageButton.active) this.nextPageButton.render(context, mouseX, mouseY, delta); - RenderSystem.enableDepthTest(); - } - - /** - * Used for drawing tooltips over truncated text - */ - private void drawTooltip(TextRenderer textRenderer, DrawContext context, Text text, int textX, int textY, int mouseX, int mouseY){ - RenderSystem.disableDepthTest(); - if (mouseX >= textX && mouseX <= textX + MAX_TEXT_WIDTH + 4 && mouseY >= textY && mouseY <= textY + 9) { - context.drawTooltip(textRenderer, text, mouseX, mouseY); - } - RenderSystem.enableDepthTest(); - } - - /** - * @see #drawTooltip(TextRenderer, DrawContext, Text, int, int, int, int) - */ - private void drawTooltip(TextRenderer textRenderer, DrawContext context, String text, int textX, int textY, int mouseX, int mouseY){ - drawTooltip(textRenderer, context, Text.of(text), textX, textY, mouseX, mouseY); - } - - /** - * Retrieves the first occurrence of section symbol formatting in a string - * - * @param string The string to fetch section symbol formatting from - * @return The section symbol and its formatting code or {@code null} if a match isn't found or if the {@code string} is null - */ - private static String getLegacyFormatting(@Nullable String string) { - if (string == null) { - return null; - } - Matcher matcher = FORMATTING_CODE_PATTERN.matcher(string); - if (matcher.find()) { - return matcher.group(0); - } - return null; - } - - public void drawTooltip(DrawContext context, int mouseX, int mouseY) { - RenderSystem.disableDepthTest(); - for (ResultButtonWidget button : resultButtons) - if (button.isMouseOver(mouseX, mouseY)) - button.renderTooltip(context, mouseX, mouseY); - RenderSystem.enableDepthTest(); - } - - public boolean mouseClicked(double mouseX, double mouseY, int mouseButton) { - for (ResultButtonWidget button : resultButtons) - if (button.mouseClicked(mouseX, mouseY, mouseButton)) { - if (button.itemStack.getNbt() == null) { - continue; - } - String internalName = button.itemStack.getNbt().getCompound("ExtraAttributes").getString("id"); - List<SkyblockCraftingRecipe> recipes = ItemRegistry.getRecipes(internalName); - if (!recipes.isEmpty()) { - this.recipeResults = recipes; - this.currentPage = 0; - this.pageCount = recipes.size(); - this.displayRecipes = true; - this.updateButtons(); - } - return true; - } - if (this.prevPageButton.mouseClicked(mouseX, mouseY, mouseButton)) { - --this.currentPage; - this.updateButtons(); - return true; - } - if (this.nextPageButton.mouseClicked(mouseX, mouseY, mouseButton)) { - ++this.currentPage; - this.updateButtons(); - return true; - } - return false; - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java deleted file mode 100644 index 29aed7a1..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/itemlist/SkyblockCraftingRecipe.java +++ /dev/null @@ -1,60 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.itemlist; - -import com.google.gson.JsonObject; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -public class SkyblockCraftingRecipe { - private static final Logger LOGGER = LoggerFactory.getLogger(SkyblockCraftingRecipe.class); - String craftText = ""; - final List<ItemStack> grid = new ArrayList<>(9); - ItemStack result; - - public static SkyblockCraftingRecipe fromJsonObject(JsonObject jsonObj) { - SkyblockCraftingRecipe recipe = new SkyblockCraftingRecipe(); - if (jsonObj.has("crafttext")) recipe.craftText = jsonObj.get("crafttext").getAsString(); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A1").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A2").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("A3").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B1").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B2").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("B3").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C1").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C2").getAsString())); - recipe.grid.add(getItemStack(jsonObj.getAsJsonObject("recipe").get("C3").getAsString())); - recipe.result = ItemRegistry.itemsMap.get(jsonObj.get("internalname").getAsString()); - return recipe; - } - - private static ItemStack getItemStack(String internalName) { - try { - if (internalName.length() > 0) { - int count = internalName.split(":").length == 1 ? 1 : Integer.parseInt(internalName.split(":")[1]); - internalName = internalName.split(":")[0]; - ItemStack itemStack = ItemRegistry.itemsMap.get(internalName).copy(); - itemStack.setCount(count); - return itemStack; - } - } catch (Exception e) { - LOGGER.error("[Skyblocker-Recipe] " + internalName, e); - } - return Items.AIR.getDefaultStack(); - } - - public List<ItemStack> getGrid() { - return grid; - } - - public ItemStack getResult() { - return result; - } - - public String getCraftText() { - return craftText; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java deleted file mode 100644 index aa933da4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNav.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.quicknav; - -import com.mojang.brigadier.exceptions.CommandSyntaxException; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.fabricmc.fabric.api.client.screen.v1.Screens; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.StringNbtReader; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.PatternSyntaxException; - -public class QuickNav { - private static final String skyblockHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}"; - private static final String dungeonHubIconNbt = "{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1605800870,415127827,-1236127084,15358548],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MWQ1YjI3M2ZmMGJjNTBjOTYwYjJjZDg2ZWVmMWM0MGExYjk0MDMyYWU3MWU3NTQ3NWE1NjhhODI1NzQyMSJ9fX0=\"}]}}}}"; - - public static void init() { - ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { - if (Utils.isOnSkyblock() && 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<>(); - SkyblockerConfig.QuickNav data = SkyblockerConfigManager.get().quickNav; - try { - if (data.button1.render) buttons.add(parseButton(data.button1, screenTitle, 0)); - if (data.button2.render) buttons.add(parseButton(data.button2, screenTitle, 1)); - if (data.button3.render) buttons.add(parseButton(data.button3, screenTitle, 2)); - if (data.button4.render) buttons.add(parseButton(data.button4, screenTitle, 3)); - if (data.button5.render) buttons.add(parseButton(data.button5, screenTitle, 4)); - if (data.button6.render) buttons.add(parseButton(data.button6, screenTitle, 5)); - if (data.button7.render) buttons.add(parseButton(data.button7, screenTitle, 6)); - if (data.button8.render) buttons.add(parseButton(data.button8, screenTitle, 7)); - if (data.button9.render) buttons.add(parseButton(data.button9, screenTitle, 8)); - 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)); - } catch (CommandSyntaxException e) { - e.printStackTrace(); - } - return buttons; - } - - private static QuickNavButton parseButton(SkyblockerConfig.QuickNavItem buttonInfo, String screenTitle, int id) throws CommandSyntaxException { - SkyblockerConfig.ItemData itemData = buttonInfo.item; - String nbtString = "{id:\"minecraft:" + itemData.itemName.toLowerCase(Locale.ROOT) + "\",Count:1"; - if (itemData.nbt.length() > 2) nbtString += "," + itemData.nbt; - nbtString += "}"; - boolean uiTitleMatches = false; - try { - uiTitleMatches = screenTitle.matches(buttonInfo.uiTitle); - } catch (PatternSyntaxException e) { - e.printStackTrace(); - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player != null) { - player.sendMessage(Text.of(Formatting.RED + "[Skyblocker] Invalid regex in quicknav button " + (id + 1) + "!"), false); - } - } - return new QuickNavButton(id, - uiTitleMatches, - buttonInfo.clickEvent, - ItemStack.fromNbt(StringNbtReader.parse(nbtString)) - ); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java deleted file mode 100644 index e41ea768..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/quicknav/QuickNavButton.java +++ /dev/null @@ -1,107 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.quicknav; - -import com.mojang.blaze3d.systems.RenderSystem; - -import me.xmrvizzy.skyblocker.mixin.accessor.HandledScreenAccessor; -import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.screen.ingame.HandledScreen; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.widget.ClickableWidget; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -@Environment(value=EnvType.CLIENT) -public class QuickNavButton extends ClickableWidget { - private static final Identifier BUTTON_TEXTURE = new Identifier("textures/gui/container/creative_inventory/tabs.png"); - - private final int index; - private boolean toggled; - private int u; - private int v; - private final String command; - private final ItemStack icon; - - public QuickNavButton(int index, boolean toggled, String command, ItemStack icon) { - super(0, 0, 26, 32, Text.empty()); - this.index = index; - this.toggled = toggled; - this.command = command; - this.icon = icon; - } - - 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); - this.u = 26; - this.v = (index < 6 ? 0 : 64) + (toggled ? 32 : 0); - } - } - - @Override - public void onClick(double mouseX, double mouseY) { - if (!this.toggled) { - this.toggled = true; - MessageScheduler.INSTANCE.sendMessageAfterCooldown(command); - // TODO : add null check with log error - } - } - - @Override - public void renderButton(DrawContext context, int mouseX, int mouseY, float delta) { - this.updateCoordinates(); - MatrixStack matrices = context.getMatrices(); - RenderSystem.disableDepthTest(); - // render button background - if (!this.toggled) { - if (this.index >= 6) - // this.drawTexture(matrices, this.x, this.y + 4, this.u, this.v + 4, this.width, this.height - 4); - context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY() + 4, this.u, this.v + 4, this.width, this.height - 4); - else - // this.drawTexture(matrices, this.x, this.y, this.u, this.v, this.width, this.height - 4); - context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY() - 2, this.u, this.v, this.width, this.height - 4); - // } else this.drawTexture(matrices, this.x, this.y, this.u, this.v, this.width, this.height); - } else { - matrices.push(); - //Move the top buttons 2 pixels up if they're selected - if (this.index < 6) matrices.translate(0f, -2f, 0f); - context.drawTexture(BUTTON_TEXTURE, this.getX(), this.getY(), this.u, this.v, this.width, this.height); - matrices.pop(); - } - // render button icon - if (!this.toggled) { - if (this.index >= 6) - // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 6); - context.drawItem(this.icon,this.getX() + 5, this.getY() + 6); - else - // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 9); - context.drawItem(this.icon,this.getX() + 5, this.getY() + 7); - } else { - if (this.index >= 6) - // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 9); - context.drawItem(this.icon,this.getX() + 5, this.getY() + 9); - else - // CLIENT.getItemRenderer().renderInGui(this.icon,this.x + 6, this.y + 6); - context.drawItem(this.icon,this.getX() + 5, this.getY() + 6); - } - RenderSystem.enableDepthTest(); - } - - @Override - protected void appendClickableNarrations(NarrationMessageBuilder builder) { - // TODO Auto-generated method stub - - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java deleted file mode 100644 index 4db5f3e6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/EffigyWaypoints.java +++ /dev/null @@ -1,71 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.text.Text; -import net.minecraft.text.TextColor; -import net.minecraft.util.DyeColor; -import net.minecraft.util.math.BlockPos; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; - -public class EffigyWaypoints { - private static final Logger LOGGER = LoggerFactory.getLogger(EffigyWaypoints.class); - private static final List<BlockPos> EFFIGIES = List.of( - new BlockPos(150, 79, 95), //Effigy 1 - new BlockPos(193, 93, 119), //Effigy 2 - new BlockPos(235, 110, 147), //Effigy 3 - new BlockPos(293, 96, 134), //Effigy 4 - new BlockPos(262, 99, 94), //Effigy 5 - new BlockPos(240, 129, 118) //Effigy 6 - ); - private static final List<BlockPos> UNBROKEN_EFFIGIES = new ArrayList<>(); - - protected static void updateEffigies() { - if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableEffigyWaypoints || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) return; - - UNBROKEN_EFFIGIES.clear(); - - try { - for (int i = 0; i < Utils.STRING_SCOREBOARD.size(); i++) { - String line = Utils.STRING_SCOREBOARD.get(i); - - if (line.contains("Effigies")) { - List<Text> effigiesText = new ArrayList<>(); - List<Text> prefixAndSuffix = Utils.TEXT_SCOREBOARD.get(i).getSiblings(); - - //Add contents of prefix and suffix to list - effigiesText.addAll(prefixAndSuffix.get(0).getSiblings()); - effigiesText.addAll(prefixAndSuffix.get(1).getSiblings()); - - for (int i2 = 1; i2 < effigiesText.size(); i2++) { - if (effigiesText.get(i2).getStyle().getColor() == TextColor.parse("gray")) UNBROKEN_EFFIGIES.add(EFFIGIES.get(i2 - 1)); - } - } - } - } catch (NullPointerException e) { - LOGGER.error("[Skyblocker] Error while updating effigies.", e); - } - } - - protected static void render(WorldRenderContext context) { - if (SkyblockerConfigManager.get().slayer.vampireSlayer.enableEffigyWaypoints && Utils.getLocation().contains("Stillgore Château")) { - for (BlockPos effigy : UNBROKEN_EFFIGIES) { - float[] colorComponents = DyeColor.RED.getColorComponents(); - if (SkyblockerConfigManager.get().slayer.vampireSlayer.compactEffigyWaypoints) { - RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy.down(6), colorComponents, 0.5F); - } else { - RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, effigy, colorComponents, 0.5F); - for (int i = 1; i < 6; i++) { - RenderHelper.renderFilledThroughWalls(context, effigy.down(i), colorComponents, 0.5F - (0.075F * i)); - } - } - } - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java deleted file mode 100644 index 78fe8f6c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/HealingMelonIndicator.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.render.title.Title; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.util.Formatting; - -public class HealingMelonIndicator { - private static final Title title = new Title("skyblocker.rift.healNow", Formatting.DARK_RED); - - public static void updateHealth() { - if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableHealingMelonIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château")) { - TitleContainer.removeTitle(title); - return; - } - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player != null && player.getHealth() <= SkyblockerConfigManager.get().slayer.vampireSlayer.healingMelonHealthThreshold * 2F) { - RenderHelper.displayInTitleContainerAndPlaySound(title); - } else { - TitleContainer.removeTitle(title); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java deleted file mode 100644 index 1fca4559..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/ManiaIndicator.java +++ /dev/null @@ -1,42 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SlayerUtils; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.render.title.Title; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer; -import net.minecraft.block.Blocks; -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.Entity; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.BlockPos; - -public class ManiaIndicator { - private static final Title title = new Title("skyblocker.rift.mania", Formatting.RED); - - protected static void updateMania() { - if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableManiaIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !(Utils.getLocation().contains("Stillgore Château")) || !SlayerUtils.isInSlayer()) { - TitleContainer.removeTitle(title); - return; - } - - Entity slayerEntity = SlayerUtils.getSlayerEntity(); - if (slayerEntity == null) return; - - boolean anyMania = false; - for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) { - if (entity.getDisplayName().toString().contains("MANIA")) { - anyMania = true; - BlockPos pos = MinecraftClient.getInstance().player.getBlockPos().down(); - boolean isGreen = MinecraftClient.getInstance().world.getBlockState(pos).getBlock() == Blocks.GREEN_TERRACOTTA; - title.setText(Text.translatable("skyblocker.rift.mania").formatted(isGreen ? Formatting.GREEN : Formatting.RED)); - RenderHelper.displayInTitleContainerAndPlaySound(title); - } - } - if (!anyMania) { - TitleContainer.removeTitle(title); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java deleted file mode 100644 index 3063f63c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/MirrorverseWaypoints.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; -import net.minecraft.util.DyeColor; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.IOException; - -public class MirrorverseWaypoints { - private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker"); - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - private static final Identifier WAYPOINTS_JSON = new Identifier(SkyblockerMod.NAMESPACE, "mirrorverse_waypoints.json"); - private static final BlockPos[] LAVA_PATH_WAYPOINTS = new BlockPos[107]; - private static final BlockPos[] UPSIDE_DOWN_WAYPOINTS = new BlockPos[66]; - private static final BlockPos[] TURBULATOR_WAYPOINTS = new BlockPos[27]; - private static final float[] COLOR_COMPONENTS = DyeColor.RED.getColorComponents(); - - static { - loadWaypoints(); - } - - /** - * Loads the waypoint locations into memory - */ - private static void loadWaypoints() { - try (BufferedReader reader = CLIENT.getResourceManager().openAsReader(WAYPOINTS_JSON)) { - JsonObject file = JsonParser.parseReader(reader).getAsJsonObject(); - JsonArray sections = file.get("sections").getAsJsonArray(); - - /// Lava Path - JsonArray lavaPathWaypoints = sections.get(0).getAsJsonObject().get("waypoints").getAsJsonArray(); - - for (int i = 0; i < lavaPathWaypoints.size(); i++) { - JsonObject point = lavaPathWaypoints.get(i).getAsJsonObject(); - LAVA_PATH_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt()); - } - - /// Upside Down Parkour - JsonArray upsideDownParkourWaypoints = sections.get(1).getAsJsonObject().get("waypoints").getAsJsonArray(); - - for (int i = 0; i < upsideDownParkourWaypoints.size(); i++) { - JsonObject point = upsideDownParkourWaypoints.get(i).getAsJsonObject(); - UPSIDE_DOWN_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt()); - } - - /// Turbulator Parkour - JsonArray turbulatorParkourWaypoints = sections.get(2).getAsJsonObject().get("waypoints").getAsJsonArray(); - - for (int i = 0; i < turbulatorParkourWaypoints.size(); i++) { - JsonObject point = turbulatorParkourWaypoints.get(i).getAsJsonObject(); - TURBULATOR_WAYPOINTS[i] = new BlockPos(point.get("x").getAsInt(), point.get("y").getAsInt(), point.get("z").getAsInt()); - } - - } catch (IOException e) { - LOGGER.info("[Skyblocker] Mirrorverse Waypoints failed to load ;("); - e.printStackTrace(); - } - } - - protected static void render(WorldRenderContext wrc) { - //I would also check for the mirrorverse location but the scoreboard stuff is not performant at all... - if (Utils.isInTheRift() && SkyblockerConfigManager.get().locations.rift.mirrorverseWaypoints) { - for (BlockPos pos : LAVA_PATH_WAYPOINTS) { - RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); - } - - for (BlockPos pos : UPSIDE_DOWN_WAYPOINTS) { - RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); - } - - for (BlockPos pos : TURBULATOR_WAYPOINTS) { - RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f); - } - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java deleted file mode 100644 index ff7298a3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/StakeIndicator.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SlayerUtils; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.render.title.Title; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer; -import net.minecraft.entity.Entity; -import net.minecraft.util.Formatting; - -public class StakeIndicator { - private static final Title title = new Title("skyblocker.rift.stakeNow", Formatting.RED); - - protected static void updateStake() { - if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableSteakStakeIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !Utils.getLocation().contains("Stillgore Château") || !SlayerUtils.isInSlayer()) { - TitleContainer.removeTitle(title); - return; - } - Entity slayerEntity = SlayerUtils.getSlayerEntity(); - if (slayerEntity != null && slayerEntity.getDisplayName().toString().contains("҉")) { - RenderHelper.displayInTitleContainerAndPlaySound(title); - } else { - TitleContainer.removeTitle(title); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java deleted file mode 100644 index f3a35869..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TheRift.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; - -public class TheRift { - /** - * @see me.xmrvizzy.skyblocker.utils.Utils#isInTheRift() Utils#isInTheRift(). - */ - public static final String LOCATION = "rift"; - - public static void init() { - WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render); - WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render); - Scheduler.INSTANCE.scheduleCyclic(EffigyWaypoints::updateEffigies, SkyblockerConfigManager.get().slayer.vampireSlayer.effigyUpdateFrequency); - Scheduler.INSTANCE.scheduleCyclic(TwinClawsIndicator::updateIce, SkyblockerConfigManager.get().slayer.vampireSlayer.holyIceUpdateFrequency); - Scheduler.INSTANCE.scheduleCyclic(ManiaIndicator::updateMania, SkyblockerConfigManager.get().slayer.vampireSlayer.maniaUpdateFrequency); - Scheduler.INSTANCE.scheduleCyclic(StakeIndicator::updateStake, SkyblockerConfigManager.get().slayer.vampireSlayer.steakStakeUpdateFrequency); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java deleted file mode 100644 index 2bd84106..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/rift/TwinClawsIndicator.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.rift; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.SlayerUtils; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import me.xmrvizzy.skyblocker.utils.render.title.Title; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.minecraft.entity.Entity; -import net.minecraft.util.Formatting; - -public class TwinClawsIndicator { - private static final Title title = new Title("skyblocker.rift.iceNow", Formatting.AQUA); - private static boolean scheduled = false; - - protected static void updateIce() { - if (!SkyblockerConfigManager.get().slayer.vampireSlayer.enableHolyIceIndicator || !Utils.isOnSkyblock() || !Utils.isInTheRift() || !(Utils.getLocation().contains("Stillgore Château")) || !SlayerUtils.isInSlayer()) { - TitleContainer.removeTitle(title); - return; - } - - Entity slayerEntity = SlayerUtils.getSlayerEntity(); - if (slayerEntity == null) return; - - boolean anyClaws = false; - for (Entity entity : SlayerUtils.getEntityArmorStands(slayerEntity)) { - if (entity.getDisplayName().toString().contains("TWINCLAWS")) { - anyClaws = true; - if (!TitleContainer.containsTitle(title) && !scheduled) { - scheduled = true; - Scheduler.INSTANCE.schedule(() -> { - RenderHelper.displayInTitleContainerAndPlaySound(title); - scheduled = false; - }, SkyblockerConfigManager.get().slayer.vampireSlayer.holyIceIndicatorTickDelay); - } - } - } - if (!anyClaws) { - TitleContainer.removeTitle(title); - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java deleted file mode 100644 index e9309706..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/Shortcuts.java +++ /dev/null @@ -1,208 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.shortcut; - -import com.google.gson.JsonObject; -import com.google.gson.reflect.TypeToken; -import com.mojang.brigadier.Command; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.fabricmc.fabric.api.client.message.v1.ClientSendMessageEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.text.Text; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.*; -import java.lang.reflect.Type; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.CompletableFuture; - -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; - -public class Shortcuts { - private static final Logger LOGGER = LoggerFactory.getLogger(Shortcuts.class); - private static final File SHORTCUTS_FILE = SkyblockerMod.CONFIG_DIR.resolve("shortcuts.json").toFile(); - @Nullable - private static CompletableFuture<Void> shortcutsLoaded; - public static final Map<String, String> commands = new HashMap<>(); - public static final Map<String, String> commandArgs = new HashMap<>(); - - public static boolean isShortcutsLoaded() { - return shortcutsLoaded != null && shortcutsLoaded.isDone(); - } - - public static void init() { - loadShortcuts(); - ClientLifecycleEvents.CLIENT_STOPPING.register(Shortcuts::saveShortcuts); - ClientCommandRegistrationCallback.EVENT.register(Shortcuts::registerCommands); - ClientSendMessageEvents.MODIFY_COMMAND.register(Shortcuts::modifyCommand); - } - - protected static void loadShortcuts() { - if (shortcutsLoaded != null && !isShortcutsLoaded()) { - return; - } - shortcutsLoaded = CompletableFuture.runAsync(() -> { - try (BufferedReader reader = new BufferedReader(new FileReader(SHORTCUTS_FILE))) { - Type shortcutsType = new TypeToken<Map<String, Map<String, String>>>() { - }.getType(); - Map<String, Map<String, String>> shortcuts = SkyblockerMod.GSON.fromJson(reader, shortcutsType); - commands.clear(); - commandArgs.clear(); - commands.putAll(shortcuts.get("commands")); - commandArgs.putAll(shortcuts.get("commandArgs")); - LOGGER.info("[Skyblocker] Loaded {} command shortcuts and {} command argument shortcuts", commands.size(), commandArgs.size()); - } catch (FileNotFoundException e) { - registerDefaultShortcuts(); - LOGGER.warn("[Skyblocker] Shortcuts file not found, using default shortcuts. This is normal when using for the first time."); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to load shortcuts file", e); - } - }); - } - - private static void registerDefaultShortcuts() { - commands.clear(); - commandArgs.clear(); - - // Skyblock - commands.put("/s", "/skyblock"); - commands.put("/i", "/is"); - commands.put("/h", "/hub"); - - // Dungeon - commands.put("/d", "/warp dungeon_hub"); - - // Chat channels - commands.put("/ca", "/chat all"); - commands.put("/cp", "/chat party"); - commands.put("/cg", "/chat guild"); - commands.put("/co", "/chat officer"); - commands.put("/cc", "/chat coop"); - - // Message - commandArgs.put("/m", "/msg"); - - // Party - commandArgs.put("/pa", "/p accept"); - commands.put("/pv", "/p leave"); - commands.put("/pd", "/p disband"); - commands.put("/rp", "/reparty"); - - // Visit - commandArgs.put("/v", "/visit"); - commands.put("/vp", "/visit portalhub"); - } - - @SuppressWarnings("unused") - private static void registerMoreDefaultShortcuts() { - // Combat - commands.put("/spider", "/warp spider"); - commands.put("/crimson", "/warp nether"); - commands.put("/end", "/warp end"); - - // Mining - commands.put("/gold", "/warp gold"); - commands.put("/cavern", "/warp deep"); - commands.put("/dwarven", "/warp mines"); - commands.put("/fo", "/warp forge"); - commands.put("/ch", "/warp crystals"); - - // Foraging & Farming - commands.put("/park", "/warp park"); - commands.put("/barn", "/warp barn"); - commands.put("/desert", "/warp desert"); - commands.put("/ga", "/warp garden"); - - // Other warps - commands.put("/castle", "/warp castle"); - commands.put("/museum", "/warp museum"); - commands.put("/da", "/warp da"); - commands.put("/crypt", "/warp crypt"); - commands.put("/nest", "/warp nest"); - commands.put("/magma", "/warp magma"); - commands.put("/void", "/warp void"); - commands.put("/drag", "/warp drag"); - commands.put("/jungle", "/warp jungle"); - commands.put("/howl", "/warp howl"); - } - - protected static void saveShortcuts(MinecraftClient client) { - JsonObject shortcutsJson = new JsonObject(); - shortcutsJson.add("commands", SkyblockerMod.GSON.toJsonTree(commands)); - shortcutsJson.add("commandArgs", SkyblockerMod.GSON.toJsonTree(commandArgs)); - try (BufferedWriter writer = new BufferedWriter(new FileWriter(SHORTCUTS_FILE))) { - SkyblockerMod.GSON.toJson(shortcutsJson, writer); - LOGGER.info("[Skyblocker] Saved {} command shortcuts and {} command argument shortcuts", commands.size(), commandArgs.size()); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to save shortcuts file", e); - } - } - - private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - for (String key : commands.keySet()) { - if (key.startsWith("/")) { - dispatcher.register(literal(key.substring(1))); - } - } - for (String key : commandArgs.keySet()) { - if (key.startsWith("/")) { - dispatcher.register(literal(key.substring(1)).then(argument("args", StringArgumentType.greedyString()))); - } - } - dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("help").executes(context -> { - FabricClientCommandSource source = context.getSource(); - String status = SkyblockerConfigManager.get().general.shortcuts.enableShortcuts && SkyblockerConfigManager.get().general.shortcuts.enableCommandShortcuts ? "§a§l (Enabled)" : "§c§l (Disabled)"; - source.sendFeedback(Text.of("§e§lSkyblocker §fCommand Shortcuts" + status)); - if (!isShortcutsLoaded()) { - source.sendFeedback(Text.translatable("skyblocker.shortcuts.notLoaded")); - } else for (Map.Entry<String, String> command : commands.entrySet()) { - source.sendFeedback(Text.of("§7" + command.getKey() + " §f→ §7" + command.getValue())); - } - status = SkyblockerConfigManager.get().general.shortcuts.enableShortcuts && SkyblockerConfigManager.get().general.shortcuts.enableCommandArgShortcuts ? "§a§l (Enabled)" : "§c§l (Disabled)"; - source.sendFeedback(Text.of("§e§lSkyblocker §fCommand Argument Shortcuts" + status)); - if (!isShortcutsLoaded()) { - source.sendFeedback(Text.translatable("skyblocker.shortcuts.notLoaded")); - } else for (Map.Entry<String, String> commandArg : commandArgs.entrySet()) { - source.sendFeedback(Text.of("§7" + commandArg.getKey() + " §f→ §7" + commandArg.getValue())); - } - source.sendFeedback(Text.of("§e§lSkyblocker §fCommands")); - for (String command : dispatcher.getSmartUsage(dispatcher.getRoot().getChild(SkyblockerMod.NAMESPACE), source).values()) { - source.sendFeedback(Text.of("§7/" + SkyblockerMod.NAMESPACE + " " + command)); - } - return Command.SINGLE_SUCCESS; - // Queue the screen or else the screen will be immediately closed after executing this command - })).then(literal("shortcuts").executes(Scheduler.queueOpenScreenCommand(ShortcutsConfigScreen::new)))); - } - - private static String modifyCommand(String command) { - if (SkyblockerConfigManager.get().general.shortcuts.enableShortcuts) { - if (!isShortcutsLoaded()) { - LOGGER.warn("[Skyblocker] Shortcuts not loaded yet, skipping shortcut for command: {}", command); - return command; - } - command = '/' + command; - if (SkyblockerConfigManager.get().general.shortcuts.enableCommandShortcuts) { - command = commands.getOrDefault(command, command); - } - if (SkyblockerConfigManager.get().general.shortcuts.enableCommandArgShortcuts) { - String[] messageArgs = command.split(" "); - for (int i = 0; i < messageArgs.length; i++) { - messageArgs[i] = commandArgs.getOrDefault(messageArgs[i], messageArgs[i]); - } - command = String.join(" ", messageArgs); - } - return command.substring(1); - } - return command; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java deleted file mode 100644 index f29470bf..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigListWidget.java +++ /dev/null @@ -1,232 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.shortcut; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.Element; -import net.minecraft.client.gui.Selectable; -import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; -import net.minecraft.client.gui.screen.narration.NarrationPart; -import net.minecraft.client.gui.widget.ElementListWidget; -import net.minecraft.client.gui.widget.TextFieldWidget; -import net.minecraft.text.Text; -import org.jetbrains.annotations.Nullable; - -import java.util.*; -import java.util.stream.Stream; - -public class ShortcutsConfigListWidget extends ElementListWidget<ShortcutsConfigListWidget.AbstractShortcutEntry> { - private final ShortcutsConfigScreen screen; - private final List<Map<String, String>> shortcutMaps = new ArrayList<>(); - - public ShortcutsConfigListWidget(MinecraftClient minecraftClient, ShortcutsConfigScreen screen, int width, int height, int top, int bottom, int itemHeight) { - super(minecraftClient, width, height, top, bottom, itemHeight); - this.screen = screen; - ShortcutCategoryEntry commandCategory = new ShortcutCategoryEntry(Shortcuts.commands, "skyblocker.shortcuts.command.target", "skyblocker.shortcuts.command.replacement"); - if (Shortcuts.isShortcutsLoaded()) { - commandCategory.shortcutsMap.keySet().stream().sorted().forEach(commandTarget -> addEntry(new ShortcutEntry(commandCategory, commandTarget))); - } else { - addEntry(new ShortcutLoadingEntry()); - } - ShortcutCategoryEntry commandArgCategory = new ShortcutCategoryEntry(Shortcuts.commandArgs, "skyblocker.shortcuts.commandArg.target", "skyblocker.shortcuts.commandArg.replacement", "skyblocker.shortcuts.commandArg.tooltip"); - if (Shortcuts.isShortcutsLoaded()) { - commandArgCategory.shortcutsMap.keySet().stream().sorted().forEach(commandArgTarget -> addEntry(new ShortcutEntry(commandArgCategory, commandArgTarget))); - } else { - addEntry(new ShortcutLoadingEntry()); - } - } - - @Override - public int getRowWidth() { - return super.getRowWidth() + 100; - } - - @Override - protected int getScrollbarPositionX() { - return super.getScrollbarPositionX() + 50; - } - - protected Optional<ShortcutCategoryEntry> getCategory() { - if (getSelectedOrNull() instanceof ShortcutCategoryEntry category) { - return Optional.of(category); - } else if (getSelectedOrNull() instanceof ShortcutEntry shortcutEntry) { - return Optional.of(shortcutEntry.category); - } - return Optional.empty(); - } - - @Override - public void setSelected(@Nullable ShortcutsConfigListWidget.AbstractShortcutEntry entry) { - super.setSelected(entry); - screen.updateButtons(); - } - - protected void addShortcutAfterSelected() { - getCategory().ifPresent(category -> children().add(children().indexOf(getSelectedOrNull()) + 1, new ShortcutEntry(category))); - } - - @Override - protected boolean removeEntry(AbstractShortcutEntry entry) { - return super.removeEntry(entry); - } - - protected boolean hasChanges() { - ShortcutEntry[] notEmptyShortcuts = getNotEmptyShortcuts().toArray(ShortcutEntry[]::new); - return notEmptyShortcuts.length != shortcutMaps.stream().mapToInt(Map::size).sum() || Arrays.stream(notEmptyShortcuts).anyMatch(ShortcutEntry::isChanged); - } - - protected void saveShortcuts() { - shortcutMaps.forEach(Map::clear); - getNotEmptyShortcuts().forEach(ShortcutEntry::save); - Shortcuts.saveShortcuts(MinecraftClient.getInstance()); // Save shortcuts to disk - } - - private Stream<ShortcutEntry> getNotEmptyShortcuts() { - return children().stream().filter(ShortcutEntry.class::isInstance).map(ShortcutEntry.class::cast).filter(ShortcutEntry::isNotEmpty); - } - - protected static abstract class AbstractShortcutEntry extends ElementListWidget.Entry<AbstractShortcutEntry> { - } - - private class ShortcutCategoryEntry extends AbstractShortcutEntry { - private final Map<String, String> shortcutsMap; - private final Text targetName; - private final Text replacementName; - @Nullable - private final Text tooltip; - - private ShortcutCategoryEntry(Map<String, String> shortcutsMap, String targetName, String replacementName) { - this(shortcutsMap, targetName, replacementName, (Text) null); - } - - private ShortcutCategoryEntry(Map<String, String> shortcutsMap, String targetName, String replacementName, String tooltip) { - this(shortcutsMap, targetName, replacementName, Text.translatable(tooltip)); - } - - private ShortcutCategoryEntry(Map<String, String> shortcutsMap, String targetName, String replacementName, @Nullable Text tooltip) { - this.shortcutsMap = shortcutsMap; - this.targetName = Text.translatable(targetName); - this.replacementName = Text.translatable(replacementName); - this.tooltip = tooltip; - shortcutMaps.add(shortcutsMap); - addEntry(this); - } - - @Override - public List<? extends Element> children() { - return List.of(); - } - - @Override - public List<? extends Selectable> selectableChildren() { - return List.of(new Selectable() { - @Override - public SelectionType getType() { - return SelectionType.HOVERED; - } - - @Override - public void appendNarrations(NarrationMessageBuilder builder) { - builder.put(NarrationPart.TITLE, targetName, replacementName); - } - }); - } - - @Override - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - context.drawCenteredTextWithShadow(client.textRenderer, targetName, width / 2 - 85, y + 5, 0xFFFFFF); - context.drawCenteredTextWithShadow(client.textRenderer, replacementName, width / 2 + 85, y + 5, 0xFFFFFF); - if (tooltip != null && isMouseOver(mouseX, mouseY)) { - screen.setTooltip(tooltip); - } - } - } - - private class ShortcutLoadingEntry extends AbstractShortcutEntry { - private final Text text; - - private ShortcutLoadingEntry() { - this.text = Text.translatable("skyblocker.shortcuts.notLoaded"); - } - - @Override - public List<? extends Element> children() { - return List.of(); - } - - @Override - public List<? extends Selectable> selectableChildren() { - return List.of(new Selectable() { - @Override - public SelectionType getType() { - return SelectionType.HOVERED; - } - - @Override - public void appendNarrations(NarrationMessageBuilder builder) { - builder.put(NarrationPart.TITLE, text); - } - }); - } - - @Override - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - context.drawCenteredTextWithShadow(client.textRenderer, text, width / 2, y + 5, 0xFFFFFF); - } - } - - protected class ShortcutEntry extends AbstractShortcutEntry { - private final List<TextFieldWidget> children; - private final ShortcutCategoryEntry category; - private final TextFieldWidget target; - private final TextFieldWidget replacement; - - private ShortcutEntry(ShortcutCategoryEntry category) { - this(category, ""); - } - - private ShortcutEntry(ShortcutCategoryEntry category, String targetString) { - this.category = category; - target = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, width / 2 - 160, 5, 150, 20, category.targetName); - replacement = new TextFieldWidget(MinecraftClient.getInstance().textRenderer, width / 2 + 10, 5, 150, 20, category.replacementName); - target.setText(targetString); - replacement.setText(category.shortcutsMap.getOrDefault(targetString, "")); - children = List.of(target, replacement); - } - - @Override - public String toString() { - return target.getText() + " → " + replacement.getText(); - } - - @Override - public List<? extends Element> children() { - return children; - } - - @Override - public List<? extends Selectable> selectableChildren() { - return children; - } - - private boolean isNotEmpty() { - return !target.getText().isEmpty() && !replacement.getText().isEmpty(); - } - - private boolean isChanged() { - return !category.shortcutsMap.containsKey(target.getText()) || !category.shortcutsMap.get(target.getText()).equals(replacement.getText()); - } - - private void save() { - category.shortcutsMap.put(target.getText(), replacement.getText()); - } - - @Override - public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { - target.setY(y); - replacement.setY(y); - target.render(context, mouseX, mouseY, tickDelta); - replacement.render(context, mouseX, mouseY, tickDelta); - context.drawCenteredTextWithShadow(client.textRenderer, "→", width / 2, y + 5, 0xFFFFFF); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java deleted file mode 100644 index d9fe850b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/shortcut/ShortcutsConfigScreen.java +++ /dev/null @@ -1,113 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.shortcut; - -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ConfirmScreen; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.tooltip.Tooltip; -import net.minecraft.client.gui.widget.ButtonWidget; -import net.minecraft.client.gui.widget.GridWidget; -import net.minecraft.client.gui.widget.SimplePositioningWidget; -import net.minecraft.screen.ScreenTexts; -import net.minecraft.text.Text; - -public class ShortcutsConfigScreen extends Screen { - - private ShortcutsConfigListWidget shortcutsConfigListWidget; - private ButtonWidget buttonDelete; - private ButtonWidget buttonNew; - private ButtonWidget buttonDone; - private boolean initialized; - private double scrollAmount; - private final Screen parent; - - public ShortcutsConfigScreen() { - this(null); - } - - public ShortcutsConfigScreen(Screen parent) { - super(Text.translatable("skyblocker.shortcuts.config")); - this.parent = parent; - } - - @Override - public void setTooltip(Text tooltip) { - super.setTooltip(tooltip); - } - - @Override - protected void init() { - super.init(); - if (initialized) { - shortcutsConfigListWidget.updateSize(width, height, 32, height - 64); - } else { - shortcutsConfigListWidget = new ShortcutsConfigListWidget(client, this, width, height, 32, height - 64, 25); - initialized = true; - } - addDrawableChild(shortcutsConfigListWidget); - GridWidget gridWidget = new GridWidget(); - gridWidget.getMainPositioner().marginX(5).marginY(2); - GridWidget.Adder adder = gridWidget.createAdder(2); - buttonDelete = ButtonWidget.builder(Text.translatable("selectServer.delete"), button -> { - if (client != null && shortcutsConfigListWidget.getSelectedOrNull() instanceof ShortcutsConfigListWidget.ShortcutEntry shortcutEntry) { - scrollAmount = shortcutsConfigListWidget.getScrollAmount(); - client.setScreen(new ConfirmScreen(this::deleteEntry, Text.translatable("skyblocker.shortcuts.deleteQuestion"), Text.translatable("skyblocker.shortcuts.deleteWarning", shortcutEntry), Text.translatable("selectServer.deleteButton"), ScreenTexts.CANCEL)); - } - }).build(); - adder.add(buttonDelete); - buttonNew = ButtonWidget.builder(Text.translatable("skyblocker.shortcuts.new"), buttonNew -> shortcutsConfigListWidget.addShortcutAfterSelected()).build(); - adder.add(buttonNew); - adder.add(ButtonWidget.builder(ScreenTexts.CANCEL, button -> { - if (client != null) { - close(); - } - }).build()); - buttonDone = ButtonWidget.builder(ScreenTexts.DONE, button -> { - shortcutsConfigListWidget.saveShortcuts(); - if (client != null) { - close(); - } - }).tooltip(Tooltip.of(Text.translatable("skyblocker.shortcuts.commandSuggestionTooltip"))).build(); - adder.add(buttonDone); - gridWidget.refreshPositions(); - SimplePositioningWidget.setPos(gridWidget, 0, this.height - 64, this.width, 64); - gridWidget.forEachChild(this::addDrawableChild); - updateButtons(); - } - - private void deleteEntry(boolean confirmedAction) { - if (client != null) { - if (confirmedAction && shortcutsConfigListWidget.getSelectedOrNull() instanceof ShortcutsConfigListWidget.ShortcutEntry shortcutEntry) { - shortcutsConfigListWidget.removeEntry(shortcutEntry); - } - client.setScreen(this); // Re-inits the screen and keeps the old instance of ShortcutsConfigListWidget - shortcutsConfigListWidget.setScrollAmount(scrollAmount); - } - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - context.drawCenteredTextWithShadow(this.textRenderer, this.title, this.width / 2, 16, 0xFFFFFF); - } - - @Override - public void close() { - if (client != null && shortcutsConfigListWidget.hasChanges()) { - client.setScreen(new ConfirmScreen(confirmedAction -> { - if (confirmedAction) { - this.client.setScreen(parent); - } else { - client.setScreen(this); - } - }, Text.translatable("text.skyblocker.quit_config"), Text.translatable("text.skyblocker.quit_config_sure"), Text.translatable("text.skyblocker.quit_discard"), ScreenTexts.CANCEL)); - } else { - this.client.setScreen(parent); - } - } - - protected void updateButtons() { - buttonDelete.active = Shortcuts.isShortcutsLoaded() && shortcutsConfigListWidget.getSelectedOrNull() instanceof ShortcutsConfigListWidget.ShortcutEntry; - buttonNew.active = Shortcuts.isShortcutsLoaded() && shortcutsConfigListWidget.getCategory().isPresent(); - buttonDone.active = Shortcuts.isShortcutsLoaded(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java deleted file mode 100644 index 84af889c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/special/SpecialEffects.java +++ /dev/null @@ -1,96 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.special; - -import com.mojang.blaze3d.systems.RenderSystem; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.enchantment.Enchantments; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.nbt.StringNbtReader; -import net.minecraft.particle.ParticleTypes; -import net.minecraft.text.Text; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class SpecialEffects { - private static final Logger LOGGER = LoggerFactory.getLogger(SpecialEffects.class); - private static final Pattern DROP_PATTERN = Pattern.compile("(?:\\[[A-Z+]+] )?(?<player>[A-Za-z0-9_]+) unlocked (?<item>.+)!"); - private static final ItemStack NECRON_HANDLE = new ItemStack(Items.STICK); - private static final ItemStack SCROLL = new ItemStack(Items.WRITABLE_BOOK); - private static ItemStack TIER_5_SKULL; - private static ItemStack FIFTH_STAR; - - static { - NECRON_HANDLE.addEnchantment(Enchantments.PROTECTION, 1); - SCROLL.addEnchantment(Enchantments.PROTECTION, 1); - try { - TIER_5_SKULL = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-1613868903,-527154034,-1445577520,748807544],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTEwZjlmMTA4NWQ0MDcxNDFlYjc3NjE3YTRhYmRhYWEwOGQ4YWYzM2I5NjAyMDBmZThjMTI2YzFkMTQ0NTY4MiJ9fX0=\"}]}}}}")); - FIFTH_STAR = ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;1904417095,756174249,-1302927470,1407004198],Properties:{textures:[{Value:\"eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzFjODA0MjUyN2Y4MWM4ZTI5M2UyODEwMTEzNDg5ZjQzOTRjYzZlZmUxNWQxYWZhYzQzMTU3MWM3M2I2MmRjNCJ9fX0=\"}]}}}}")); - } catch (Exception e) { - TIER_5_SKULL = ItemStack.EMPTY; - FIFTH_STAR = ItemStack.EMPTY; - LOGGER.error("[Skyblocker Special Effects] Failed to parse NBT for a player head!", e); - } - } - - public static void init() { - ClientReceiveMessageEvents.GAME.register(SpecialEffects::displayRareDropEffect); - } - - private static void displayRareDropEffect(Text message, boolean overlay) { - //We don't check if we're in dungeons because that check doesn't work in m7 which defeats the point of this - //It might also allow it to work with Croesus - if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().general.specialEffects.rareDungeonDropEffects) { - try { - String stringForm = message.getString(); - Matcher matcher = DROP_PATTERN.matcher(stringForm); - - if (matcher.matches()) { - MinecraftClient client = MinecraftClient.getInstance(); - String player = matcher.group("player"); - - if (player.equals(client.getSession().getUsername())) { - ItemStack stack = getStackFromName(matcher.group("item")); - - if (!stack.isEmpty()) { - if (RenderSystem.isOnRenderThread()) { - client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30); - client.gameRenderer.showFloatingItem(stack); - } else { - RenderSystem.recordRenderCall(() -> { - client.particleManager.addEmitter(client.player, ParticleTypes.PORTAL, 30); - client.gameRenderer.showFloatingItem(stack); - }); - } - } - } - } - } catch (Exception e) { //In case there's a regex failure or something else bad happens - LOGGER.error("[Skyblocker Special Effects] An unexpected exception was encountered: ", e); - } - } - } - - private static ItemStack getStackFromName(String itemName) { - return switch (itemName) { - //M7 - case "Necron Dye" -> new ItemStack(Items.ORANGE_DYE); - case "Dark Claymore" -> new ItemStack(Items.STONE_SWORD); - case "Necron's Handle", "Shiny Necron's Handle" -> NECRON_HANDLE; - case "Enchanted Book (Thunderlord VII)" -> new ItemStack(Items.ENCHANTED_BOOK); - case "Master Skull - Tier 5" -> TIER_5_SKULL; - case "Shadow Warp", "Wither Shield", "Implosion" -> SCROLL; - case "Fifth Master Star" -> FIFTH_STAR; - - //M6 - case "Giant's Sword" -> new ItemStack(Items.IRON_SWORD); - - default -> ItemStack.EMPTY; - }; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java deleted file mode 100644 index 994317f8..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/spidersden/Relics.java +++ /dev/null @@ -1,171 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.spidersden; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.mojang.brigadier.CommandDispatcher; -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.PosUtils; -import me.xmrvizzy.skyblocker.utils.Utils; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.command.CommandRegistryAccess; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.text.Text; -import net.minecraft.util.DyeColor; -import net.minecraft.util.Identifier; -import net.minecraft.util.math.BlockPos; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; - -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; - -public class Relics { - private static final Logger LOGGER = LoggerFactory.getLogger(Relics.class); - private static CompletableFuture<Void> relicsLoaded; - @SuppressWarnings({"unused", "FieldCanBeLocal"}) - private static int totalRelics = 0; - private static final List<BlockPos> relics = new ArrayList<>(); - private static final Map<String, Set<BlockPos>> foundRelics = new HashMap<>(); - - public static void init() { - ClientLifecycleEvents.CLIENT_STARTED.register(Relics::loadRelics); - ClientLifecycleEvents.CLIENT_STOPPING.register(Relics::saveFoundRelics); - ClientCommandRegistrationCallback.EVENT.register(Relics::registerCommands); - WorldRenderEvents.AFTER_TRANSLUCENT.register(Relics::render); - ClientReceiveMessageEvents.GAME.register(Relics::onChatMessage); - } - - private static void loadRelics(MinecraftClient client) { - relicsLoaded = CompletableFuture.runAsync(() -> { - try (BufferedReader reader = client.getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "spidersden/relics.json"))) { - for (Map.Entry<String, JsonElement> json : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { - if (json.getKey().equals("total")) { - totalRelics = json.getValue().getAsInt(); - } else if (json.getKey().equals("locations")) { - for (JsonElement locationJson : json.getValue().getAsJsonArray().asList()) { - JsonObject posData = locationJson.getAsJsonObject(); - relics.add(new BlockPos(posData.get("x").getAsInt(), posData.get("y").getAsInt(), posData.get("z").getAsInt())); - } - } - } - LOGGER.info("[Skyblocker] Loaded relics locations"); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to load relics locations", e); - } - - try (BufferedReader reader = new BufferedReader(new FileReader(SkyblockerMod.CONFIG_DIR.resolve("found_relics.json").toFile()))) { - for (Map.Entry<String, JsonElement> profileJson : JsonParser.parseReader(reader).getAsJsonObject().asMap().entrySet()) { - Set<BlockPos> foundRelicsForProfile = new HashSet<>(); - for (JsonElement foundRelicsJson : profileJson.getValue().getAsJsonArray().asList()) { - foundRelicsForProfile.add(PosUtils.parsePosString(foundRelicsJson.getAsString())); - } - foundRelics.put(profileJson.getKey(), foundRelicsForProfile); - } - LOGGER.debug("[Skyblocker] Loaded found relics"); - } catch (FileNotFoundException ignored) { - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to load found relics", e); - } - }); - } - - private static void saveFoundRelics(MinecraftClient client) { - try (BufferedWriter writer = new BufferedWriter(new FileWriter(SkyblockerMod.CONFIG_DIR.resolve("found_relics.json").toFile()))) { - JsonObject json = new JsonObject(); - for (Map.Entry<String, Set<BlockPos>> foundRelicsForProfile : foundRelics.entrySet()) { - JsonArray foundRelicsJson = new JsonArray(); - for (BlockPos foundRelic : foundRelicsForProfile.getValue()) { - foundRelicsJson.add(PosUtils.getPosString(foundRelic)); - } - json.add(foundRelicsForProfile.getKey(), foundRelicsJson); - } - SkyblockerMod.GSON.toJson(json, writer); - LOGGER.debug("[Skyblocker] Saved found relics"); - } catch (IOException e) { - LOGGER.error("[Skyblocker] Failed to write found relics to file", e); - } - } - - private static void registerCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) { - dispatcher.register(literal(SkyblockerMod.NAMESPACE) - .then(literal("relics") - .then(literal("markAllFound").executes(context -> { - Relics.markAllFound(); - context.getSource().sendFeedback(Text.translatable("skyblocker.relics.markAllFound")); - return 1; - })) - .then(literal("markAllMissing").executes(context -> { - Relics.markAllMissing(); - context.getSource().sendFeedback(Text.translatable("skyblocker.relics.markAllMissing")); - return 1; - })))); - } - - private static void render(WorldRenderContext context) { - SkyblockerConfig.Relics config = SkyblockerConfigManager.get().locations.spidersDen.relics; - - if (config.enableRelicsHelper && relicsLoaded.isDone() && Utils.getLocationRaw().equals("combat_1")) { - for (BlockPos fairySoulPos : relics) { - boolean isRelicMissing = isRelicMissing(fairySoulPos); - if (!isRelicMissing && !config.highlightFoundRelics) continue; - float[] colorComponents = isRelicMissing ? DyeColor.YELLOW.getColorComponents() : DyeColor.BROWN.getColorComponents(); - RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, fairySoulPos, colorComponents, 0.5F); - } - } - } - - private static void onChatMessage(Text text, boolean overlay) { - String message = text.getString(); - if (message.equals("You've already found this relic!") || message.startsWith("+10,000 Coins! (") && message.endsWith("/28 Relics)")) { - markClosestRelicFound(); - } - } - - private static void markClosestRelicFound() { - if (!relicsLoaded.isDone()) return; - PlayerEntity player = MinecraftClient.getInstance().player; - if (player == null) { - LOGGER.warn("[Skyblocker] Failed to mark closest relic as found because player is null"); - return; - } - relics.stream() - .filter(Relics::isRelicMissing) - .min(Comparator.comparingDouble(relicPos -> relicPos.getSquaredDistance(player.getPos()))) - .filter(relicPos -> relicPos.getSquaredDistance(player.getPos()) <= 16) - .ifPresent(relicPos -> { - foundRelics.computeIfAbsent(Utils.getProfile(), profileKey -> new HashSet<>()); - foundRelics.get(Utils.getProfile()).add(relicPos); - }); - } - - private static boolean isRelicMissing(BlockPos relicPos) { - Set<BlockPos> foundRelicsForProfile = foundRelics.get(Utils.getProfile()); - return foundRelicsForProfile == null || !foundRelicsForProfile.contains(relicPos); - } - - private static void markAllFound() { - foundRelics.computeIfAbsent(Utils.getProfile(), profileKey -> new HashSet<>()); - foundRelics.get(Utils.getProfile()).addAll(relics); - } - - private static void markAllMissing() { - Set<BlockPos> foundRelicsForProfile = foundRelics.get(Utils.getProfile()); - if (foundRelicsForProfile != null) { - foundRelicsForProfile.clear(); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java deleted file mode 100644 index 8f18e367..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/TabHud.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud; - -import org.lwjgl.glfw.GLFW; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; -import net.minecraft.client.option.KeyBinding; -import net.minecraft.client.util.InputUtil; - -public class TabHud { - - public static KeyBinding toggleB; - public static KeyBinding toggleA; - // public static KeyBinding mapTgl; - public static KeyBinding defaultTgl; - - public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Tab HUD"); - - public static void init() { - - toggleB = KeyBindingHelper.registerKeyBinding( - new KeyBinding("key.skyblocker.toggleB", - InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_B, - "key.categories.skyblocker")); - toggleA = KeyBindingHelper.registerKeyBinding( - new KeyBinding("key.skyblocker.toggleA", - InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_N, - "key.categories.skyblocker")); - defaultTgl = KeyBindingHelper.registerKeyBinding( - new KeyBinding("key.skyblocker.defaultTgl", - InputUtil.Type.KEYSYM, - GLFW.GLFW_KEY_M, - "key.categories.skyblocker")); - - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java deleted file mode 100644 index f02c7dc6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenBuilder.java +++ /dev/null @@ -1,179 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder; - -import java.io.BufferedReader; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.NoSuchElementException; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.AlignStage; -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.CollideStage; -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PipelineStage; -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.PlaceStage; -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline.StackStage; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.DungeonPlayerWidget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.ErrorWidget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.EventWidget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.util.Identifier; - -public class ScreenBuilder { - - // layout pipeline - private final ArrayList<PipelineStage> layoutPipeline = new ArrayList<>(); - - // all widget instances this builder knows - private final ArrayList<Widget> instances = new ArrayList<>(); - // maps alias -> widget instance - private final HashMap<String, Widget> objectMap = new HashMap<>(); - - private final String builderName; - - /** - * Create a ScreenBuilder from a json. - */ - public ScreenBuilder(Identifier ident) { - - try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager().openAsReader(ident)) { - this.builderName = ident.getPath(); - - JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); - - JsonArray widgets = json.getAsJsonArray("widgets"); - JsonArray layout = json.getAsJsonArray("layout"); - - for (JsonElement w : widgets) { - JsonObject widget = w.getAsJsonObject(); - String name = widget.get("name").getAsString(); - String alias = widget.get("alias").getAsString(); - - Widget wid = instanceFrom(name, widget); - objectMap.put(alias, wid); - instances.add(wid); - } - - for (JsonElement l : layout) { - PipelineStage ps = createStage(l.getAsJsonObject()); - layoutPipeline.add(ps); - } - } catch (Exception ex) { - // rethrow as unchecked exception so that I don't have to catch anything in the ScreenMaster - throw new IllegalStateException("Failed to load file " + ident + ". Reason: " + ex.getMessage()); - } - } - - /** - * Try to find a class in the widget package that has the supplied name and - * call it's constructor. Manual work is required if the class has arguments. - */ - public Widget instanceFrom(String name, JsonObject widget) { - - // do widgets that require args the normal way - JsonElement arg; - switch (name) { - case "EventWidget" -> { - return new EventWidget(widget.get("inGarden").getAsBoolean()); - } - case "DungeonPlayerWidget" -> { - return new DungeonPlayerWidget(widget.get("player").getAsInt()); - } - case "ErrorWidget" -> { - arg = widget.get("text"); - if (arg == null) { - return new ErrorWidget(); - } else { - return new ErrorWidget(arg.getAsString()); - } - } - case "Widget" -> - // clown case sanity check. don't instantiate the superclass >:| - throw new NoSuchElementException(builderName + "[ERROR]: No such Widget type \"Widget\"!"); - } - - // reflect something together for the "normal" ones. - - // list all packages that might contain widget classes - // using Package isn't reliable, as some classes might not be loaded yet, - // causing the packages not to show. - String packbase = "me.xmrvizzy.skyblocker.skyblock.tabhud.widget"; - String[] packnames = { - packbase, - packbase + ".rift" - }; - - // construct the full class name and try to load. - Class<?> clazz = null; - for (String pn : packnames) { - try { - clazz = Class.forName(pn + "." + name); - } catch (LinkageError | ClassNotFoundException ex) { - continue; - } - } - - // load failed. - if (clazz == null) { - throw new NoSuchElementException(builderName + "/[ERROR]: No such Widget type \"" + name + "\"!"); - } - - // return instance of that class. - try { - Constructor<?> ctor = clazz.getConstructor(); - return (Widget) ctor.newInstance(); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException - | IllegalArgumentException | InvocationTargetException | SecurityException ex) { - throw new IllegalStateException(builderName + "/" + name + ": Internal error..."); - } - } - - /** - * Create a PipelineStage from a json object. - */ - public PipelineStage createStage(JsonObject descr) throws NoSuchElementException { - - String op = descr.get("op").getAsString(); - - return switch (op) { - case "place" -> new PlaceStage(this, descr); - case "stack" -> new StackStage(this, descr); - case "align" -> new AlignStage(this, descr); - case "collideAgainst" -> new CollideStage(this, descr); - default -> throw new NoSuchElementException("No such op " + op + " as requested by " + this.builderName); - }; - } - - /** - * Lookup Widget instance from alias name - */ - public Widget getInstance(String name) { - if (!this.objectMap.containsKey(name)) { - throw new NoSuchElementException("No widget with alias " + name + " in screen " + builderName); - } - return this.objectMap.get(name); - } - - /** - * Run the pipeline to build a Screen - */ - public void run(DrawContext context, int screenW, int screenH) { - - for (Widget w : instances) { - w.update(); - } - for (PipelineStage ps : layoutPipeline) { - ps.run(screenW, screenH); - } - for (Widget w : instances) { - w.render(context); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java deleted file mode 100644 index 194ab3d4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/ScreenMaster.java +++ /dev/null @@ -1,144 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder; - -import java.io.BufferedReader; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.TabHud; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerLocator; -import net.fabricmc.fabric.api.resource.ResourceManagerHelper; -import net.fabricmc.fabric.api.resource.ResourcePackActivationType; -import net.fabricmc.fabric.api.resource.SimpleSynchronousResourceReloadListener; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.resource.Resource; -import net.minecraft.resource.ResourceManager; -import net.minecraft.resource.ResourceType; -import net.minecraft.util.Identifier; - -public class ScreenMaster { - - private static final Logger LOGGER = LoggerFactory.getLogger("skyblocker"); - - private static final int VERSION = 1; - - private static final HashMap<String, ScreenBuilder> standardMap = new HashMap<>(); - private static final HashMap<String, ScreenBuilder> screenAMap = new HashMap<>(); - private static final HashMap<String, ScreenBuilder> screenBMap = new HashMap<>(); - - /** - * Load a screen mapping from an identifier - */ - public static void load(Identifier ident) { - - String path = ident.getPath(); - String[] parts = path.split("/"); - String screenType = parts[parts.length - 2]; - String location = parts[parts.length - 1]; - location = location.replace(".json", ""); - - ScreenBuilder sb = new ScreenBuilder(ident); - switch (screenType) { - case "standard" -> standardMap.put(location, sb); - case "screen_a" -> screenAMap.put(location, sb); - case "screen_b" -> screenBMap.put(location, sb); - } - } - - /** - * Top level render method. - * Calls the appropriate ScreenBuilder with the screen's dimensions - */ - public static void render(DrawContext context, int w, int h) { - String location = PlayerLocator.getPlayerLocation().internal; - HashMap<String, ScreenBuilder> lookup; - if (TabHud.toggleA.isPressed()) { - lookup = screenAMap; - } else if (TabHud.toggleB.isPressed()) { - lookup = screenBMap; - } else { - lookup = standardMap; - } - - ScreenBuilder sb = lookup.get(location); - // seems suboptimal, maybe load the default first into all possible values - // and then override? - if (sb == null) { - sb = lookup.get("default"); - } - - sb.run(context, w, h); - - } - - public static void init() { - - // WHY MUST IT ALWAYS BE SUCH NESTED GARBAGE MINECRAFT KEEP THAT IN DFU FFS - - FabricLoader.getInstance() - .getModContainer("skyblocker") - .ifPresent(container -> ResourceManagerHelper.registerBuiltinResourcePack( - new Identifier("skyblocker", "top_aligned"), - container, - ResourcePackActivationType.NORMAL)); - - ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener( - // ...why are we instantiating an interface again? - new SimpleSynchronousResourceReloadListener() { - @Override - public Identifier getFabricId() { - return new Identifier("skyblocker", "tabhud"); - } - - @Override - public void reload(ResourceManager manager) { - - standardMap.clear(); - screenAMap.clear(); - screenBMap.clear(); - - int excnt = 0; - - for (Map.Entry<Identifier, Resource> entry : manager - .findResources("tabhud", path -> path.getPath().endsWith("version.json")) - .entrySet()) { - - try (BufferedReader reader = MinecraftClient.getInstance().getResourceManager() - .openAsReader(entry.getKey())) { - JsonObject json = JsonParser.parseReader(reader).getAsJsonObject(); - if (json.get("format_version").getAsInt() != VERSION) { - throw new IllegalStateException(String.format("Resource pack isn't compatible! Expected version %d, got %d", VERSION, json.get("format_version").getAsInt())); - } - - } catch (Exception ex) { - throw new IllegalStateException( - "Rejected this resource pack. Reason: " + ex.getMessage()); - } - } - - for (Map.Entry<Identifier, Resource> entry : manager - .findResources("tabhud", path -> path.getPath().endsWith(".json") && !path.getPath().endsWith("version.json")) - .entrySet()) { - try { - - load(entry.getKey()); - } catch (Exception e) { - LOGGER.error(e.getMessage()); - excnt++; - } - } - if (excnt > 0) { - throw new IllegalStateException("This screen definition isn't valid, see above"); - } - } - }); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java deleted file mode 100644 index 76789a4c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/AlignStage.java +++ /dev/null @@ -1,83 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline; - -import java.util.ArrayList; -import java.util.NoSuchElementException; - -import com.google.gson.JsonObject; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; - -public class AlignStage extends PipelineStage { - - private enum AlignReference { - HORICENT("horizontalCenter"), - VERTCENT("verticalCenter"), - LEFTCENT("leftOfCenter"), - RIGHTCENT("rightOfCenter"), - TOPCENT("topOfCenter"), - BOTCENT("botOfCenter"), - TOP("top"), - BOT("bot"), - LEFT("left"), - RIGHT("right"); - - private final String str; - - AlignReference(String d) { - this.str = d; - } - - public static AlignReference parse(String s) throws NoSuchElementException { - for (AlignReference d : AlignReference.values()) { - if (d.str.equals(s)) { - return d; - } - } - throw new NoSuchElementException("\"" + s + "\" is not a valid reference for an align op!"); - } - } - - private final AlignReference reference; - - public AlignStage(ScreenBuilder builder, JsonObject descr) { - this.reference = AlignReference.parse(descr.get("reference").getAsString()); - this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") - .asList() - .stream() - .map(x -> builder.getInstance(x.getAsString())) - .toList()); - } - - public void run(int screenW, int screenH) { - int wHalf, hHalf; - for (Widget wid : primary) { - switch (this.reference) { - case HORICENT -> wid.setX((screenW - wid.getWidth()) / 2); - case VERTCENT -> wid.setY((screenH - wid.getHeight()) / 2); - case LEFTCENT -> { - wHalf = screenW / 2; - wid.setX(wHalf - ScreenConst.WIDGET_PAD_HALF - wid.getWidth()); - } - case RIGHTCENT -> { - wHalf = screenW / 2; - wid.setX(wHalf + ScreenConst.WIDGET_PAD_HALF); - } - case TOPCENT -> { - hHalf = screenH / 2; - wid.setY(hHalf - ScreenConst.WIDGET_PAD_HALF - wid.getHeight()); - } - case BOTCENT -> { - hHalf = screenH / 2; - wid.setY(hHalf + ScreenConst.WIDGET_PAD_HALF); - } - case TOP -> wid.setY(ScreenConst.getScreenPad()); - case BOT -> wid.setY(screenH - wid.getHeight() - ScreenConst.getScreenPad()); - case LEFT -> wid.setX(ScreenConst.getScreenPad()); - case RIGHT -> wid.setX(screenW - wid.getWidth() - ScreenConst.getScreenPad()); - } - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java deleted file mode 100644 index b6a5f789..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/CollideStage.java +++ /dev/null @@ -1,153 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline; - -import java.util.ArrayList; -import java.util.NoSuchElementException; - -import com.google.gson.JsonObject; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; - -public class CollideStage extends PipelineStage { - - private enum CollideDirection { - LEFT("left"), - RIGHT("right"), - TOP("top"), - BOT("bot"); - - private final String str; - - CollideDirection(String d) { - this.str = d; - } - - public static CollideDirection parse(String s) throws NoSuchElementException { - for (CollideDirection d : CollideDirection.values()) { - if (d.str.equals(s)) { - return d; - } - } - throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a collide op!"); - } - } - - private final CollideDirection direction; - - public CollideStage(ScreenBuilder builder, JsonObject descr) { - this.direction = CollideDirection.parse(descr.get("direction").getAsString()); - this.primary = new ArrayList<>(descr.getAsJsonArray("widgets") - .asList() - .stream() - .map(x -> builder.getInstance(x.getAsString())) - .toList()); - this.secondary = new ArrayList<>(descr.getAsJsonArray("colliders") - .asList() - .stream() - .map(x -> builder.getInstance(x.getAsString())) - .toList()); - } - - public void run(int screenW, int screenH) { - switch (this.direction) { - case LEFT -> primary.forEach(w -> collideAgainstL(screenW, w)); - case RIGHT -> primary.forEach(w -> collideAgainstR(screenW, w)); - case TOP -> primary.forEach(w -> collideAgainstT(screenH, w)); - case BOT -> primary.forEach(w -> collideAgainstB(screenH, w)); - } - } - - public void collideAgainstL(int screenW, Widget w) { - int yMin = w.getY(); - int yMax = w.getY() + w.getHeight(); - - int xCor = screenW; - - for (Widget other : secondary) { - if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) { - // too high, next one - continue; - } - - if (other.getY() - ScreenConst.WIDGET_PAD > yMax) { - // too low, next - continue; - } - - int xPos = other.getX() - ScreenConst.WIDGET_PAD - w.getWidth(); - xCor = Math.min(xCor, xPos); - } - w.setX(xCor); - } - - public void collideAgainstR(int screenW, Widget w) { - int yMin = w.getY(); - int yMax = w.getY() + w.getHeight(); - - int xCor = 0; - - for (Widget other : secondary) { - if (other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD < yMin) { - // too high, next one - continue; - } - - if (other.getY() - ScreenConst.WIDGET_PAD > yMax) { - // too low, next - continue; - } - - int xPos = other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD; - xCor = Math.max(xCor, xPos); - } - w.setX(xCor); - } - - public void collideAgainstT(int screenH, Widget w) { - int xMin = w.getX(); - int xMax = w.getX() + w.getWidth(); - - int yCor = screenH; - - for (Widget other : secondary) { - if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) { - // too far left, next one - continue; - } - - if (other.getX() - ScreenConst.WIDGET_PAD > xMax) { - // too far right, next - continue; - } - - int yPos = other.getY() - ScreenConst.WIDGET_PAD - w.getHeight(); - yCor = Math.min(yCor, yPos); - } - w.setY(yCor); - } - - public void collideAgainstB(int screenH, Widget w) { - int xMin = w.getX(); - int xMax = w.getX() + w.getWidth(); - - int yCor = 0; - - for (Widget other : secondary) { - if (other.getX() + other.getWidth() + ScreenConst.WIDGET_PAD < xMin) { - // too far left, next one - continue; - } - - if (other.getX() - ScreenConst.WIDGET_PAD > xMax) { - // too far right, next - continue; - } - - int yPos = other.getY() + other.getHeight() + ScreenConst.WIDGET_PAD; - yCor = Math.max(yCor, yPos); - } - w.setY(yCor); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java deleted file mode 100644 index e560058c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PipelineStage.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline; - -import java.util.ArrayList; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; - -public abstract class PipelineStage { - - protected ArrayList<Widget> primary = null; - protected ArrayList<Widget> secondary = null; - - public abstract void run(int screenW, int screenH); - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java deleted file mode 100644 index a950f8f2..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/PlaceStage.java +++ /dev/null @@ -1,94 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline; - -import java.util.ArrayList; -import java.util.NoSuchElementException; - -import com.google.gson.JsonObject; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; - -public class PlaceStage extends PipelineStage { - - private enum PlaceLocation { - CENTER("center"), - TOPCENT("centerTop"), - BOTCENT("centerBot"), - LEFTCENT("centerLeft"), - RIGHTCENT("centerRight"), - TRCORNER("cornerTopRight"), - TLCORNER("cornerTopLeft"), - BRCORNER("cornerBotRight"), - BLCORNER("cornerBotLeft"); - - private final String str; - - PlaceLocation(String d) { - this.str = d; - } - - public static PlaceLocation parse(String s) throws NoSuchElementException { - for (PlaceLocation d : PlaceLocation.values()) { - if (d.str.equals(s)) { - return d; - } - } - throw new NoSuchElementException("\"" + s + "\" is not a valid location for a place op!"); - } - } - - private final PlaceLocation where; - - public PlaceStage(ScreenBuilder builder, JsonObject descr) { - this.where = PlaceLocation.parse(descr.get("where").getAsString()); - this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") - .asList() - .stream() - .map(x -> builder.getInstance(x.getAsString())) - .limit(1) - .toList()); - } - - public void run(int screenW, int screenH) { - Widget wid = primary.get(0); - switch (where) { - case CENTER -> { - wid.setX((screenW - wid.getWidth()) / 2); - wid.setY((screenH - wid.getHeight()) / 2); - } - case TOPCENT -> { - wid.setX((screenW - wid.getWidth()) / 2); - wid.setY(ScreenConst.getScreenPad()); - } - case BOTCENT -> { - wid.setX((screenW - wid.getWidth()) / 2); - wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); - } - case LEFTCENT -> { - wid.setX(ScreenConst.getScreenPad()); - wid.setY((screenH - wid.getHeight()) / 2); - } - case RIGHTCENT -> { - wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); - wid.setY((screenH - wid.getHeight()) / 2); - } - case TLCORNER -> { - wid.setX(ScreenConst.getScreenPad()); - wid.setY(ScreenConst.getScreenPad()); - } - case TRCORNER -> { - wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); - wid.setY(ScreenConst.getScreenPad()); - } - case BLCORNER -> { - wid.setX(ScreenConst.getScreenPad()); - wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); - } - case BRCORNER -> { - wid.setX((screenW - wid.getWidth()) - ScreenConst.getScreenPad()); - wid.setY((screenH - wid.getHeight()) - ScreenConst.getScreenPad()); - } - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java deleted file mode 100644 index 078927ef..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/screenbuilder/pipeline/StackStage.java +++ /dev/null @@ -1,114 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.pipeline; - -import java.util.ArrayList; -import java.util.NoSuchElementException; - -import com.google.gson.JsonObject; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.ScreenConst; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; - -public class StackStage extends PipelineStage { - - private enum StackDirection { - HORIZONTAL("horizontal"), - VERTICAL("vertical"); - - private final String str; - - StackDirection(String d) { - this.str = d; - } - - public static StackDirection parse(String s) throws NoSuchElementException { - for (StackDirection d : StackDirection.values()) { - if (d.str.equals(s)) { - return d; - } - } - throw new NoSuchElementException("\"" + s + "\" is not a valid direction for a stack op!"); - } - } - - private enum StackAlign { - TOP("top"), - BOT("bot"), - LEFT("left"), - RIGHT("right"), - CENTER("center"); - - private final String str; - - StackAlign(String d) { - this.str = d; - } - - public static StackAlign parse(String s) throws NoSuchElementException { - for (StackAlign d : StackAlign.values()) { - if (d.str.equals(s)) { - return d; - } - } - throw new NoSuchElementException("\"" + s + "\" is not a valid alignment for a stack op!"); - } - } - - private final StackDirection direction; - private final StackAlign align; - - public StackStage(ScreenBuilder builder, JsonObject descr) { - this.direction = StackDirection.parse(descr.get("direction").getAsString()); - this.align = StackAlign.parse(descr.get("align").getAsString()); - this.primary = new ArrayList<>(descr.getAsJsonArray("apply_to") - .asList() - .stream() - .map(x -> builder.getInstance(x.getAsString())) - .toList()); - } - - public void run(int screenW, int screenH) { - switch (this.direction) { - case HORIZONTAL -> stackWidgetsHoriz(screenW); - case VERTICAL -> stackWidgetsVert(screenH); - } - } - - public void stackWidgetsVert(int screenH) { - int compHeight = -ScreenConst.WIDGET_PAD; - for (Widget wid : primary) { - compHeight += wid.getHeight() + 5; - } - - int y = switch (this.align) { - - case TOP -> ScreenConst.getScreenPad(); - case BOT -> (screenH - compHeight) - ScreenConst.getScreenPad(); - default -> (screenH - compHeight) / 2; - }; - - for (Widget wid : primary) { - wid.setY(y); - y += wid.getHeight() + ScreenConst.WIDGET_PAD; - } - } - - public void stackWidgetsHoriz(int screenW) { - int compWidth = -ScreenConst.WIDGET_PAD; - for (Widget wid : primary) { - compWidth += wid.getWidth() + ScreenConst.WIDGET_PAD; - } - - int x = switch (this.align) { - - case LEFT -> ScreenConst.getScreenPad(); - case RIGHT -> (screenW - compWidth) - ScreenConst.getScreenPad(); - default -> (screenW - compWidth) / 2; - }; - - for (Widget wid : primary) { - wid.setX(x); - x += wid.getWidth() + ScreenConst.WIDGET_PAD; - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java deleted file mode 100644 index 97237769..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/Ico.java +++ /dev/null @@ -1,60 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.util; - -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; - -/** - * Stores convenient shorthands for common ItemStack definitions - */ -public class Ico { - public static final ItemStack MAP = new ItemStack(Items.FILLED_MAP); - public static final ItemStack NTAG = new ItemStack(Items.NAME_TAG); - public static final ItemStack EMERALD = new ItemStack(Items.EMERALD); - public static final ItemStack CLOCK = new ItemStack(Items.CLOCK); - public static final ItemStack DIASWORD = new ItemStack(Items.DIAMOND_SWORD); - public static final ItemStack DBUSH = new ItemStack(Items.DEAD_BUSH); - public static final ItemStack VILLAGER = new ItemStack(Items.VILLAGER_SPAWN_EGG); - public static final ItemStack MOREGOLD = new ItemStack(Items.GOLDEN_APPLE); - public static final ItemStack COMPASS = new ItemStack(Items.COMPASS); - public static final ItemStack SUGAR = new ItemStack(Items.SUGAR); - public static final ItemStack HOE = new ItemStack(Items.IRON_HOE); - public static final ItemStack GOLD = new ItemStack(Items.GOLD_INGOT); - public static final ItemStack BONE = new ItemStack(Items.BONE); - public static final ItemStack SIGN = new ItemStack(Items.OAK_SIGN); - public static final ItemStack FISH_ROD = new ItemStack(Items.FISHING_ROD); - public static final ItemStack SWORD = new ItemStack(Items.IRON_SWORD); - public static final ItemStack LANTERN = new ItemStack(Items.LANTERN); - public static final ItemStack COOKIE = new ItemStack(Items.COOKIE); - public static final ItemStack POTION = new ItemStack(Items.POTION); - public static final ItemStack BARRIER = new ItemStack(Items.BARRIER); - public static final ItemStack PLAYER = new ItemStack(Items.PLAYER_HEAD); - public static final ItemStack WATER = new ItemStack(Items.WATER_BUCKET); - public static final ItemStack LEATHER = new ItemStack(Items.LEATHER); - public static final ItemStack MITHRIL = new ItemStack(Items.PRISMARINE_CRYSTALS); - public static final ItemStack REDSTONE = new ItemStack(Items.REDSTONE); - public static final ItemStack FIRE = new ItemStack(Items.CAMPFIRE); - public static final ItemStack STRING = new ItemStack(Items.STRING); - public static final ItemStack WITHER = new ItemStack(Items.WITHER_SKELETON_SKULL); - public static final ItemStack FLESH = new ItemStack(Items.ROTTEN_FLESH); - public static final ItemStack DRAGON = new ItemStack(Items.DRAGON_HEAD); - public static final ItemStack DIAMOND = new ItemStack(Items.DIAMOND); - public static final ItemStack ICE = new ItemStack(Items.ICE); - public static final ItemStack CHEST = new ItemStack(Items.CHEST); - public static final ItemStack COMMAND = new ItemStack(Items.COMMAND_BLOCK); - public static final ItemStack SKULL = new ItemStack(Items.SKELETON_SKULL); - public static final ItemStack BOOK = new ItemStack(Items.WRITABLE_BOOK); - public static final ItemStack FURNACE = new ItemStack(Items.FURNACE); - public static final ItemStack CHESTPLATE = new ItemStack(Items.IRON_CHESTPLATE); - public static final ItemStack B_ROD = new ItemStack(Items.BLAZE_ROD); - public static final ItemStack BOW = new ItemStack(Items.BOW); - public static final ItemStack COPPER = new ItemStack(Items.COPPER_INGOT); - public static final ItemStack COMPOSTER = new ItemStack(Items.COMPOSTER); - public static final ItemStack SAPLING = new ItemStack(Items.OAK_SAPLING); - public static final ItemStack MILESTONE = new ItemStack(Items.LODESTONE); - public static final ItemStack PICKAXE = new ItemStack(Items.IRON_PICKAXE); - public static final ItemStack NETHER_STAR = new ItemStack(Items.NETHER_STAR); - public static final ItemStack HEART_OF_THE_SEA = new ItemStack(Items.HEART_OF_THE_SEA); - public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE); - public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE); - public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java deleted file mode 100644 index 446b7d81..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerListMgr.java +++ /dev/null @@ -1,172 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.util; - -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import me.xmrvizzy.skyblocker.mixin.accessor.PlayerListHudAccessor; - -import me.xmrvizzy.skyblocker.utils.Utils; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.client.network.PlayerListEntry; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; - -/** - * This class may be used to get data from the player list. It doesn't get its - * data every frame, instead, a scheduler is used to update the data this class - * is holding periodically. The list is sorted like in the vanilla game. - */ -public class PlayerListMgr { - - public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Regex"); - - private static List<PlayerListEntry> playerList; - private static String footer; - - public static void updateList() { - - if (!Utils.isOnSkyblock()) { - return; - } - - ClientPlayNetworkHandler cpnwh = MinecraftClient.getInstance().getNetworkHandler(); - - // check is needed, else game crash on server leave - if (cpnwh != null) { - playerList = cpnwh.getPlayerList().stream().sorted(PlayerListHudAccessor.getOrdering()).toList(); - } - } - - public static void updateFooter(Text f) { - if (f == null) { - footer = null; - } else { - footer = f.getString(); - } - } - - public static String getFooter() { - return footer; - } - - /** - * Get the display name at some index of the player list and apply a pattern to - * it - * - * @return the matcher if p fully matches, else null - */ - public static Matcher regexAt(int idx, Pattern p) { - - String str = PlayerListMgr.strAt(idx); - - if (str == null) { - return null; - } - - Matcher m = p.matcher(str); - if (!m.matches()) { - LOGGER.error("no match: \"{}\" against \"{}\"", str, p); - return null; - } else { - return m; - } - } - - /** - * Get the display name at some index of the player list as string - * - * @return the string or null, if the display name is null, empty or whitespace - * only - */ - public static String strAt(int idx) { - - if (playerList == null) { - return null; - } - - if (playerList.size() <= idx) { - return null; - } - - Text txt = playerList.get(idx).getDisplayName(); - if (txt == null) { - return null; - } - String str = txt.getString().trim(); - if (str.isEmpty()) { - return null; - } - return str; - } - - /** - * Gets the display name at some index of the player list - * - * @return the text or null, if the display name is null - * - * @implNote currently designed specifically for crimson isles faction quests - * widget and the rift widgets, might not work correctly without - * modification for other stuff. you've been warned! - */ - public static Text textAt(int idx) { - - if (playerList == null) { - return null; - } - - if (playerList.size() <= idx) { - return null; - } - - Text txt = playerList.get(idx).getDisplayName(); - if (txt == null) { - return null; - } - - // Rebuild the text object to remove leading space thats in all faction quest - // stuff (also removes trailing space just in case) - MutableText newText = Text.empty(); - int size = txt.getSiblings().size(); - - for (int i = 0; i < size; i++) { - Text current = txt.getSiblings().get(i); - String textToAppend = current.getString(); - - // Trim leading & trailing space - this can only be done at the start and end - // otherwise it'll produce malformed results - if (i == 0) - textToAppend = textToAppend.stripLeading(); - if (i == size - 1) - textToAppend = textToAppend.stripTrailing(); - - newText.append(Text.literal(textToAppend).setStyle(current.getStyle())); - } - - // Avoid returning an empty component - Rift advertisements needed this - if (newText.getString().isEmpty()) { - return null; - } - - return newText; - } - - /** - * Get the display name at some index of the player list as Text as seen in the - * game - * - * @return the PlayerListEntry at that index - */ - public static PlayerListEntry getRaw(int idx) { - return playerList.get(idx); - } - - public static int getSize() { - return playerList.size(); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java deleted file mode 100644 index 11c9d860..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/PlayerLocator.java +++ /dev/null @@ -1,87 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.util; - -import me.xmrvizzy.skyblocker.utils.Utils; - -/** - * Uses data from the player list to determine the area the player is in. - */ -public class PlayerLocator { - - public enum Location { - DUNGEON("dungeon"), - GUEST_ISLAND("guest_island"), - HOME_ISLAND("home_island"), - CRIMSON_ISLE("crimson_isle"), - DUNGEON_HUB("dungeon_hub"), - FARMING_ISLAND("farming_island"), - PARK("park"), - DWARVEN_MINES("dwarven_mines"), - CRYSTAL_HOLLOWS("crystal_hollows"), - END("end"), - GOLD_MINE("gold_mine"), - DEEP_CAVERNS("deep_caverns"), - HUB("hub"), - SPIDER_DEN("spider_den"), - JERRY("jerry_workshop"), - GARDEN("garden"), - INSTANCED("kuudra"), - THE_RIFT("rift"), - DARK_AUCTION("dark_auction"), - UNKNOWN("unknown"); - - public final String internal; - - Location(String i) { - // as used internally by the mod, e.g. in the json - this.internal = i; - } - - } - - public static Location getPlayerLocation() { - - if (!Utils.isOnSkyblock()) { - return Location.UNKNOWN; - } - - String areaDescriptor = PlayerListMgr.strAt(41); - - if (areaDescriptor == null || areaDescriptor.length() < 6) { - return Location.UNKNOWN; - } - - if (areaDescriptor.startsWith("Dungeon")) { - return Location.DUNGEON; - } - - return switch (areaDescriptor.substring(6)) { - case "Private Island" -> { - String islandType = PlayerListMgr.strAt(44); - if (islandType == null) { - yield Location.UNKNOWN; - } else if (islandType.endsWith("Guest")) { - yield Location.GUEST_ISLAND; - } else { - yield Location.HOME_ISLAND; - } - } - case "Crimson Isle" -> Location.CRIMSON_ISLE; - case "Dungeon Hub" -> Location.DUNGEON_HUB; - case "The Farming Islands" -> Location.FARMING_ISLAND; - case "The Park" -> Location.PARK; - case "Dwarven Mines" -> Location.DWARVEN_MINES; - case "Crystal Hollows" -> Location.CRYSTAL_HOLLOWS; - case "The End" -> Location.END; - case "Gold Mine" -> Location.GOLD_MINE; - case "Deep Caverns" -> Location.DEEP_CAVERNS; - case "Hub" -> Location.HUB; - case "Spider's Den" -> Location.SPIDER_DEN; - case "Jerry's Workshop" -> Location.JERRY; - case "Garden" -> Location.GARDEN; - case "Instanced" -> Location.INSTANCED; - case "The Rift" -> Location.THE_RIFT; - case "Dark Auction" -> Location.DARK_AUCTION; - default -> Location.UNKNOWN; - }; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java deleted file mode 100644 index c230548b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/util/ScreenConst.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.util; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; - -public class ScreenConst { - public static final int WIDGET_PAD = 5; - public static final int WIDGET_PAD_HALF = 3; - private static final int SCREEN_PAD_BASE = 20; - - public static int getScreenPad() { - return (int) ((1f/((float)SkyblockerConfigManager.get().general.tabHud.tabHudScale/100f) * SCREEN_PAD_BASE)); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java deleted file mode 100644 index 7e44bc44..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CameraPositionWidget.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.MathHelper; - -public class CameraPositionWidget extends Widget { - private static final MutableText TITLE = Text.literal("Camera Pos").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - - public CameraPositionWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - double yaw = CLIENT.getCameraEntity().getYaw(); - double pitch = CLIENT.getCameraEntity().getPitch(); - - this.addComponent( - new PlainTextComponent(Text.literal("Yaw: " + roundToDecimalPlaces(MathHelper.wrapDegrees(yaw), 3)))); - this.addComponent(new PlainTextComponent( - Text.literal("Pitch: " + roundToDecimalPlaces(MathHelper.wrapDegrees(pitch), 3)))); - - } - - // https://stackoverflow.com/a/33889423 - private static double roundToDecimalPlaces(double value, int decimalPlaces) { - double shift = Math.pow(10, decimalPlaces); - - return Math.round(value * shift) / shift; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java deleted file mode 100644 index 8bbdb25f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CommsWidget.java +++ /dev/null @@ -1,63 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.MathHelper; - -// this widget shows the status of the king's commissions. -// (dwarven mines and crystal hollows) - -public class CommsWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - // match a comm - // group 1: comm name - // group 2: comm progress (without "%" for comms that show a percentage) - private static final Pattern COMM_PATTERN = Pattern.compile("(?<name>.*): (?<progress>.*)%?"); - - public CommsWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - for (int i = 50; i <= 53; i++) { - Matcher m = PlayerListMgr.regexAt(i, COMM_PATTERN); - // end of comms found? - if (m == null) { - if (i == 50) { - this.addComponent(new IcoTextComponent()); - } - break; - } - - ProgressComponent pc; - - String name = m.group("name"); - String progress = m.group("progress"); - - if (progress.equals("DONE")) { - pc = new ProgressComponent(Ico.BOOK, Text.of(name), Text.of(progress), 100f, pcntToCol(100)); - } else { - float pcnt = Float.parseFloat(progress.substring(0, progress.length() - 1)); - pc = new ProgressComponent(Ico.BOOK, Text.of(name), pcnt, pcntToCol(pcnt)); - } - this.addComponent(pc); - } - } - - private int pcntToCol(float pcnt) { - return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java deleted file mode 100644 index 1307b407..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ComposterWidget.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about the garden's composter - -public class ComposterWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Composter").formatted(Formatting.GREEN, - Formatting.BOLD); - - public ComposterWidget() { - super(TITLE, Formatting.GREEN.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.SAPLING, "Organic Matter:", Formatting.YELLOW, 48); - this.addSimpleIcoText(Ico.FURNACE, "Fuel:", Formatting.BLUE, 49); - this.addSimpleIcoText(Ico.CLOCK, "Time Left:", Formatting.RED, 50); - this.addSimpleIcoText(Ico.COMPOSTER, "Stored Compost:", Formatting.DARK_GREEN, 51); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java deleted file mode 100644 index bf1d086f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/CookieWidget.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about active super cookies -// or not, if you're unwilling to buy one - -public class CookieWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Cookie Info").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - private static final Pattern COOKIE_PATTERN = Pattern.compile(".*\\nCookie Buff\\n(?<buff>.*)\\n"); - - public CookieWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - String footertext = PlayerListMgr.getFooter(); - if (footertext == null || !footertext.contains("Cookie Buff")) { - this.addComponent(new IcoTextComponent()); - return; - } - - Matcher m = COOKIE_PATTERN.matcher(footertext); - if (!m.find() || m.group("buff") == null) { - this.addComponent(new IcoTextComponent()); - return; - } - - String buff = m.group("buff"); - if (buff.startsWith("Not")) { - this.addComponent(new IcoTextComponent(Ico.COOKIE, Text.of("Not active"))); - } else { - Text cookie = Text.literal("Time Left: ").append(buff); - this.addComponent(new IcoTextComponent(Ico.COOKIE, cookie)); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java deleted file mode 100644 index 1d056e0c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonBuffWidget.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.Arrays; -import java.util.Comparator; - -// this widget shows a list of obtained dungeon buffs - -public class DungeonBuffWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Dungeon Buffs").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - public DungeonBuffWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - - String footertext = PlayerListMgr.getFooter(); - - if (footertext == null || !footertext.contains("Dungeon Buffs")) { - this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY))); - return; - } - - String interesting = footertext.split("Dungeon Buffs")[1]; - String[] lines = interesting.split("\n"); - - if (!lines[1].startsWith("Blessing")) { - this.addComponent(new PlainTextComponent(Text.literal("No buffs found!").formatted(Formatting.GRAY))); - return; - } - - //Filter out text unrelated to blessings - lines = Arrays.stream(lines).filter(s -> s.contains("Blessing")).toArray(String[]::new); - - //Alphabetically sort the blessings - Arrays.sort(lines, Comparator.comparing(String::toLowerCase)); - - for (String line : lines) { - if (line.length() < 3) { // empty line is §s - break; - } - int color = getBlessingColor(line); - this.addComponent(new PlainTextComponent(Text.literal(line).styled(style -> style.withColor(color)))); - } - - } - - @SuppressWarnings("DataFlowIssue") - public int getBlessingColor(String blessing) { - if (blessing.contains("Life")) return Formatting.LIGHT_PURPLE.getColorValue(); - if (blessing.contains("Power")) return Formatting.RED.getColorValue(); - if (blessing.contains("Stone")) return Formatting.GREEN.getColorValue(); - if (blessing.contains("Time")) return 0xafb8c1; - if (blessing.contains("Wisdom")) return Formatting.AQUA.getColorValue(); - - return 0xffffff; - } - -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java deleted file mode 100644 index 80134b66..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDeathWidget.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows various dungeon info -// deaths, healing, dmg taken, milestones - -public class DungeonDeathWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Death").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - // match the deaths entry - // group 1: amount of deaths - private static final Pattern DEATH_PATTERN = Pattern.compile("Team Deaths: (?<deathnum>\\d+).*"); - - public DungeonDeathWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - Matcher m = PlayerListMgr.regexAt(25, DEATH_PATTERN); - if (m == null) { - this.addComponent(new IcoTextComponent()); - } else { - Formatting f = (m.group("deathnum").equals("0")) ? Formatting.GREEN : Formatting.RED; - Text d = Widget.simpleEntryText(m.group("deathnum"), "Deaths: ", f); - IcoTextComponent deaths = new IcoTextComponent(Ico.SKULL, d); - this.addComponent(deaths); - } - - this.addSimpleIcoText(Ico.SWORD, "Damage Dealt:", Formatting.RED, 26); - this.addSimpleIcoText(Ico.POTION, "Healing Done:", Formatting.RED, 27); - this.addSimpleIcoText(Ico.NTAG, "Milestone:", Formatting.YELLOW, 28); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java deleted file mode 100644 index 2ca617ee..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonDownedWidget.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about... something? -// related to downed people in dungeons, not sure what this is supposed to show - -public class DungeonDownedWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Downed").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - public DungeonDownedWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - String down = PlayerListMgr.strAt(21); - if (down == null) { - this.addComponent(new IcoTextComponent()); - } else { - - Formatting format = Formatting.RED; - if (down.endsWith("NONE")) { - format = Formatting.GRAY; - } - int idx = down.indexOf(": "); - Text downed = (idx == -1) ? null - : Widget.simpleEntryText(down.substring(idx + 2), "Downed: ", format); - IcoTextComponent d = new IcoTextComponent(Ico.SKULL, downed); - this.addComponent(d); - } - - this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GRAY, 22); - this.addSimpleIcoText(Ico.POTION, "Revive:", Formatting.GRAY, 23); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java deleted file mode 100644 index 2ba0c0cc..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPlayerWidget.java +++ /dev/null @@ -1,103 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about a player in the current dungeon group - -public class DungeonPlayerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Player").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - // match a player entry - // group 1: name - // group 2: class (or literal "EMPTY" pre dungeon start) - // group 3: level (or nothing, if pre dungeon start) - // this regex filters out the ironman icon as well as rank prefixes and emblems - // \[\d*\] (?:\[[A-Za-z]+\] )?(?<name>[A-Za-z0-9_]*) (?:.* )?\((?<class>\S*) ?(?<level>[LXVI]*)\) - private static final Pattern PLAYER_PATTERN = Pattern - .compile("\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?(?<name>[A-Za-z0-9_]*) (?:.* )?\\((?<class>\\S*) ?(?<level>[LXVI]*)\\)"); - - private static final HashMap<String, ItemStack> ICOS = new HashMap<>(); - private static final ArrayList<String> MSGS = new ArrayList<>(); - static { - ICOS.put("Tank", Ico.CHESTPLATE); - ICOS.put("Mage", Ico.B_ROD); - ICOS.put("Berserk", Ico.DIASWORD); - ICOS.put("Archer", Ico.BOW); - ICOS.put("Healer", Ico.POTION); - - MSGS.add("PRESS A TO JOIN"); - MSGS.add("Invite a friend!"); - MSGS.add("But nobody came."); - MSGS.add("More is better!"); - } - - private final int player; - - // title needs to be changeable here - public DungeonPlayerWidget(int player) { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - this.player = player; - } - - @Override - public void updateContent() { - int start = 1 + (player - 1) * 4; - - if (PlayerListMgr.strAt(start) == null) { - int idx = player - 2; - IcoTextComponent noplayer = new IcoTextComponent(Ico.SIGN, - Text.literal(MSGS.get(idx)).formatted(Formatting.GRAY)); - this.addComponent(noplayer); - return; - } - Matcher m = PlayerListMgr.regexAt(start, PLAYER_PATTERN); - if (m == null) { - this.addComponent(new IcoTextComponent()); - this.addComponent(new IcoTextComponent()); - } else { - - Text name = Text.literal("Name: ").append(Text.literal(m.group("name")).formatted(Formatting.YELLOW)); - this.addComponent(new IcoTextComponent(Ico.PLAYER, name)); - - String cl = m.group("class"); - String level = m.group("level"); - - if (level == null) { - PlainTextComponent ptc = new PlainTextComponent( - Text.literal("Player is dead").formatted(Formatting.RED)); - this.addComponent(ptc); - } else { - - Formatting clf = Formatting.GRAY; - ItemStack cli = Ico.BARRIER; - if (!cl.equals("EMPTY")) { - cli = ICOS.get(cl); - clf = Formatting.LIGHT_PURPLE; - cl += " " + m.group("level"); - } - - Text clazz = Text.literal("Class: ").append(Text.literal(cl).formatted(clf)); - IcoTextComponent itclass = new IcoTextComponent(cli, clazz); - this.addComponent(itclass); - } - } - - this.addSimpleIcoText(Ico.CLOCK, "Ult Cooldown:", Formatting.GOLD, start + 1); - this.addSimpleIcoText(Ico.POTION, "Revives:", Formatting.DARK_PURPLE, start + 2); - - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java deleted file mode 100644 index ef765fc3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonPuzzleWidget.java +++ /dev/null @@ -1,57 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about all puzzeles in the dungeon (name and status) - -public class DungeonPuzzleWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Puzzles").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - // match a puzzle entry - // group 1: name - // group 2: status - // " ?.*" to diescard the solver's name if present - // the teleport maze has a trailing whitespace that messes with the regex - private static final Pattern PUZZLE_PATTERN = Pattern.compile("(?<name>.*): \\[(?<status>.*)\\] ?.*"); - - public DungeonPuzzleWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - int pos = 48; - - while (pos < 60) { - Matcher m = PlayerListMgr.regexAt(pos, PUZZLE_PATTERN); - if (m == null) { - break; - } - Text t = Text.literal(m.group("name") + ": ") - .append(Text.literal("[").formatted(Formatting.GRAY)) - .append(m.group("status")) - .append(Text.literal("]").formatted(Formatting.GRAY)); - IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, t); - this.addComponent(itc); - pos++; - // code points for puzzle status chars unsolved and solved: 10022, 10004 - // not sure which one is which - // still need to find out codepoint for the puzzle failed char - } - if (pos == 48) { - this.addComponent( - new IcoTextComponent(Ico.BARRIER, Text.literal("No puzzles!").formatted(Formatting.GRAY))); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java deleted file mode 100644 index a985b4b1..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java +++ /dev/null @@ -1,26 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about the secrets of the dungeon - -public class DungeonSecretWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - public DungeonSecretWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31); - this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java deleted file mode 100644 index 05b88127..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/DungeonServerWidget.java +++ /dev/null @@ -1,48 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows broad info about the current dungeon -// opened/completed rooms, % of secrets found and time taken - -public class DungeonServerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Dungeon Info").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - // match the secrets text - // group 1: % of secrets found (without "%") - private static final Pattern SECRET_PATTERN = Pattern.compile("Secrets Found: (?<secnum>.*)%"); - - public DungeonServerWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.AQUA, 41); - this.addSimpleIcoText(Ico.SIGN, "Rooms Visited:", Formatting.DARK_PURPLE, 42); - this.addSimpleIcoText(Ico.SIGN, "Rooms Completed:", Formatting.LIGHT_PURPLE, 43); - - Matcher m = PlayerListMgr.regexAt(44, SECRET_PATTERN); - if (m == null) { - this.addComponent(new ProgressComponent()); - } else { - ProgressComponent scp = new ProgressComponent(Ico.CHEST, Text.of("Secrets found:"), - Float.parseFloat(m.group("secnum")), - Formatting.DARK_PURPLE.getColorValue()); - this.addComponent(scp); - } - - this.addSimpleIcoText(Ico.CLOCK, "Time:", Formatting.GOLD, 45); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java deleted file mode 100644 index 718ea2d3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EffectWidget.java +++ /dev/null @@ -1,67 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widgte shows, how many active effects you have. -// it also shows one of those in detail. -// the parsing is super suspect and should be replaced by some regexes sometime later - -public class EffectWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Effect Info").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - public EffectWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - - String footertext = PlayerListMgr.getFooter(); - - if (footertext == null || !footertext.contains("Active Effects")) { - this.addComponent(new IcoTextComponent()); - return; - - } - - String[] lines = footertext.split("Active Effects")[1].split("\n"); - if (lines.length < 2) { - this.addComponent(new IcoTextComponent()); - return; - } - - if (lines[1].startsWith("No")) { - Text txt = Text.literal("No effects active").formatted(Formatting.GRAY); - this.addComponent(new IcoTextComponent(Ico.POTION, txt)); - } else if (lines[1].contains("God")) { - String timeleft = lines[1].split("! ")[1]; - Text godpot = Text.literal("God potion!").formatted(Formatting.RED); - Text txttleft = Text.literal(timeleft).formatted(Formatting.LIGHT_PURPLE); - IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, godpot, txttleft); - this.addComponent(iftc); - } else { - String number = lines[1].substring("You have ".length()); - int idx = number.indexOf(' '); - if (idx == -1 || lines.length < 4) { - this.addComponent(new IcoFatTextComponent()); - return; - } - number = number.substring(0, idx); - Text active = Text.literal("Active Effects: ") - .append(Text.literal(number).formatted(Formatting.YELLOW)); - - IcoFatTextComponent iftc = new IcoFatTextComponent(Ico.POTION, active, - Text.literal(lines[3]).formatted(Formatting.AQUA)); - this.addComponent(iftc); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java deleted file mode 100644 index 0f858fb6..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ElectionWidget.java +++ /dev/null @@ -1,104 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows the status or results of the current election - -public class ElectionWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Election Info").formatted(Formatting.YELLOW, - Formatting.BOLD); - - private static final HashMap<String, ItemStack> MAYOR_DATA = new HashMap<>(); - - private static final Text EL_OVER = Text.literal("Election ") - .append(Text.literal("over!").formatted(Formatting.RED)); - - // pattern matching a candidate while people are voting - // group 1: name - // group 2: % of votes - private static final Pattern VOTE_PATTERN = Pattern.compile("(?<mayor>\\S*): \\|+ \\((?<pcnt>\\d*)%\\)"); - - static { - MAYOR_DATA.put("Aatrox", Ico.DIASWORD); - MAYOR_DATA.put("Cole", Ico.PICKAXE); - MAYOR_DATA.put("Diana", Ico.BONE); - MAYOR_DATA.put("Diaz", Ico.GOLD); - MAYOR_DATA.put("Finnegan", Ico.HOE); - MAYOR_DATA.put("Foxy", Ico.SUGAR); - MAYOR_DATA.put("Paul", Ico.COMPASS); - MAYOR_DATA.put("Scorpius", Ico.MOREGOLD); - MAYOR_DATA.put("Jerry", Ico.VILLAGER); - MAYOR_DATA.put("Derpy", Ico.DBUSH); - MAYOR_DATA.put("Marina", Ico.FISH_ROD); - } - - private static final Formatting[] COLS = { Formatting.GOLD, Formatting.RED, Formatting.LIGHT_PURPLE }; - - public ElectionWidget() { - super(TITLE, Formatting.YELLOW.getColorValue()); - } - - @Override - public void updateContent() { - String status = PlayerListMgr.strAt(76); - if (status == null) { - this.addComponent(new IcoTextComponent()); - this.addComponent(new IcoTextComponent()); - this.addComponent(new IcoTextComponent()); - this.addComponent(new IcoTextComponent()); - return; - } - - if (status.contains("Over!")) { - // election is over - IcoTextComponent over = new IcoTextComponent(Ico.BARRIER, EL_OVER); - this.addComponent(over); - - String win = PlayerListMgr.strAt(77); - if (win == null || !win.contains(": ")) { - this.addComponent(new IcoTextComponent()); - } else { - String winnername = win.split(": ")[1]; - Text winnertext = Widget.simpleEntryText(winnername, "Winner: ", Formatting.GREEN); - IcoTextComponent winner = new IcoTextComponent(MAYOR_DATA.get(winnername), winnertext); - this.addComponent(winner); - } - - this.addSimpleIcoText(Ico.PLAYER, "Participants:", Formatting.AQUA, 78); - this.addSimpleIcoText(Ico.SIGN, "Year:", Formatting.LIGHT_PURPLE, 79); - - } else { - // election is going on - this.addSimpleIcoText(Ico.CLOCK, "End in:", Formatting.GOLD, 76); - - for (int i = 77; i <= 79; i++) { - Matcher m = PlayerListMgr.regexAt(i, VOTE_PATTERN); - if (m == null) { - this.addComponent(new ProgressComponent()); - } else { - - String mayorname = m.group("mayor"); - String pcntstr = m.group("pcnt"); - float pcnt = Float.parseFloat(pcntstr); - Text candidate = Text.literal(mayorname).formatted(COLS[i - 77]); - ProgressComponent pc = new ProgressComponent(MAYOR_DATA.get(mayorname), candidate, pcnt, - COLS[i - 77].getColorValue()); - this.addComponent(pc); - } - } - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ErrorWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ErrorWidget.java deleted file mode 100644 index 7f53fde7..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ErrorWidget.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// empty widget for when nothing can be shown - -public class ErrorWidget extends Widget { - private static final MutableText TITLE = Text.literal("Error").formatted(Formatting.RED, - Formatting.BOLD); - - Text error = Text.of("No info available!"); - - public ErrorWidget() { - super(TITLE, Formatting.RED.getColorValue()); - } - - public ErrorWidget(String error) { - super(TITLE, Formatting.RED.getColorValue()); - this.error = Text.of(error); - } - - @Override - public void updateContent() { - PlainTextComponent inf = new PlainTextComponent(this.error); - this.addComponent(inf); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java deleted file mode 100644 index c503d89f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EssenceWidget.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows your dungeon essences (dungeon hub only) - -public class EssenceWidget extends Widget { - - private Text undead, wither, diamond, gold, dragon, spider, ice, crimson; - - private static final MutableText TITLE = Text.literal("Essences").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public EssenceWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - wither = Widget.simpleEntryText(46, "Wither:", Formatting.DARK_PURPLE); - spider = Widget.simpleEntryText(47, "Spider:", Formatting.DARK_PURPLE); - undead = Widget.simpleEntryText(48, "Undead:", Formatting.DARK_PURPLE); - dragon = Widget.simpleEntryText(49, "Dragon:", Formatting.DARK_PURPLE); - gold = Widget.simpleEntryText(50, "Gold:", Formatting.DARK_PURPLE); - diamond = Widget.simpleEntryText(51, "Diamond:", Formatting.DARK_PURPLE); - ice = Widget.simpleEntryText(52, "Ice:", Formatting.DARK_PURPLE); - crimson = Widget.simpleEntryText(53, "Crimson:", Formatting.DARK_PURPLE); - - TableComponent tc = new TableComponent(2, 4, Formatting.DARK_AQUA.getColorValue()); - - tc.addToCell(0, 0, new IcoTextComponent(Ico.WITHER, wither)); - tc.addToCell(0, 1, new IcoTextComponent(Ico.STRING, spider)); - tc.addToCell(0, 2, new IcoTextComponent(Ico.FLESH, undead)); - tc.addToCell(0, 3, new IcoTextComponent(Ico.DRAGON, dragon)); - tc.addToCell(1, 0, new IcoTextComponent(Ico.GOLD, gold)); - tc.addToCell(1, 1, new IcoTextComponent(Ico.DIAMOND, diamond)); - tc.addToCell(1, 2, new IcoTextComponent(Ico.ICE, ice)); - tc.addToCell(1, 3, new IcoTextComponent(Ico.REDSTONE, crimson)); - this.addComponent(tc); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java deleted file mode 100644 index 7d07ad75..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/EventWidget.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about ongoing events (e.g. election) - -public class EventWidget extends Widget { - private static final MutableText TITLE = Text.literal("Event Info").formatted(Formatting.YELLOW, Formatting.BOLD); - - private final boolean isInGarden; - - public EventWidget(boolean isInGarden) { - super(TITLE, Formatting.YELLOW.getColorValue()); - this.isInGarden = isInGarden; - } - - @Override - public void updateContent() { - // hypixel devs carefully inserting the most random edge cases #317: - // the event info is placed a bit differently when in the garden. - int offset = (isInGarden) ? -1 : 0; - - this.addSimpleIcoText(Ico.NTAG, "Name:", Formatting.YELLOW, 73 + offset); - - // this could look better - Text time = Widget.plainEntryText(74 + offset); - IcoTextComponent t = new IcoTextComponent(Ico.CLOCK, time); - this.addComponent(t); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java deleted file mode 100644 index ba6a0ec1..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/FireSaleWidget.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.Formatting; - -// this widget shows info about fire sales when in the hub. -// or not, if there isn't one going on - -public class FireSaleWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Fire Sale").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - // matches a fire sale item - // group 1: item name - // group 2: # items available - // group 3: # items available in total (1 digit + "k") - private static final Pattern FIRE_PATTERN = Pattern.compile("(?<item>.*): (?<avail>\\d*)/(?<total>[0-9.]*)k"); - - public FireSaleWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - String event = PlayerListMgr.strAt(46); - - if (event == null) { - this.addComponent(new PlainTextComponent(Text.literal("No Fire Sale!").formatted(Formatting.GRAY))); - return; - } - - if (event.contains("Starts In")) { - this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.DARK_AQUA, 46); - return; - } - - for (int i = 46;; i++) { - Matcher m = PlayerListMgr.regexAt( i, FIRE_PATTERN); - if (m == null) { - break; - } - String avail = m.group("avail"); - Text itemTxt = Text.literal(m.group("item")); - float total = Float.parseFloat(m.group("total")) * 1000; - Text prgressTxt = Text.literal(String.format("%s/%.0f", avail, total)); - float pcnt = (Float.parseFloat(avail) / (total)) * 100f; - ProgressComponent pc = new ProgressComponent(Ico.GOLD, itemTxt, prgressTxt, pcnt, pcntToCol(pcnt)); - this.addComponent(pc); - } - - } - - private int pcntToCol(float pcnt) { - return MathHelper.hsvToRgb( pcnt / 300f, 0.9f, 0.9f); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java deleted file mode 100644 index ed87d496..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ForgeWidget.java +++ /dev/null @@ -1,81 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows what you're forging right now. -// for locked slots, the unlock requirement is shown - -public class ForgeWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Forge Status").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public ForgeWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - int forgestart = 54; - // why is it forges and not looms >:( - String pos = PlayerListMgr.strAt(53); - if (pos == null) { - this.addComponent(new IcoTextComponent()); - return; - } - - if (!pos.startsWith("Forges")) { - forgestart += 2; - } - - for (int i = forgestart, slot = 1; i < forgestart + 5 && i < 60; i++, slot++) { - String fstr = PlayerListMgr.strAt(i); - if (fstr == null || fstr.length() < 3) { - if (i == forgestart) { - this.addComponent(new IcoTextComponent()); - } - break; - } - Component c; - Text l1, l2; - - switch (fstr.substring(3)) { - case "LOCKED" -> { - l1 = Text.literal("Locked").formatted(Formatting.RED); - l2 = switch (slot) { - case 3 -> Text.literal("Needs HotM 3").formatted(Formatting.GRAY); - case 4 -> Text.literal("Needs HotM 4").formatted(Formatting.GRAY); - case 5 -> Text.literal("Needs PotM 2").formatted(Formatting.GRAY); - default -> - Text.literal("This message should not appear").formatted(Formatting.RED, Formatting.BOLD); - }; - c = new IcoFatTextComponent(Ico.BARRIER, l1, l2); - } - case "EMPTY" -> { - l1 = Text.literal("Empty").formatted(Formatting.GRAY); - c = new IcoTextComponent(Ico.FURNACE, l1); - } - default -> { - String[] parts = fstr.split(": "); - if (parts.length != 2) { - c = new IcoFatTextComponent(); - } else { - l1 = Text.literal(parts[0].substring(3)).formatted(Formatting.YELLOW); - l2 = Text.literal("Done in: ").formatted(Formatting.GRAY).append(Text.literal(parts[1]).formatted(Formatting.WHITE)); - c = new IcoFatTextComponent(Ico.FIRE, l1, l2); - } - } - } - this.addComponent(c); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java deleted file mode 100644 index 2fd81873..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenServerWidget.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about the garden server - -public class GardenServerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - // match the next visitor in the garden - // group 1: visitor name - private static final Pattern VISITOR_PATTERN = Pattern.compile("Next Visitor: (?<vis>.*)"); - - public GardenServerWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); - this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); - this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); - this.addSimpleIcoText(Ico.COPPER, "Copper:", Formatting.GOLD, 44); - - Matcher m = PlayerListMgr.regexAt(45, VISITOR_PATTERN); - if (m == null ) { - this.addComponent(new IcoTextComponent()); - return; - } - - String vis = m.group("vis"); - Formatting col; - if (vis.equals("Not Unlocked!")) { - col = Formatting.RED; - } else { - col = Formatting.GREEN; - } - Text visitor = Widget.simpleEntryText(vis, "Next Visitor: ", col); - IcoTextComponent v = new IcoTextComponent(Ico.PLAYER, visitor); - this.addComponent(v); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java deleted file mode 100644 index 2b928cba..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenSkillsWidget.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about your skills while in the garden - -public class GardenSkillsWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW, - Formatting.BOLD); - - // match the skill entry - // group 1: skill name and level - // group 2: progress to next level (without "%") - private static final Pattern SKILL_PATTERN = Pattern - .compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%"); - // same, but with leading space - private static final Pattern MS_PATTERN = Pattern.compile("\\S*: (?<skill>[A-Za-z]* [0-9]*): (?<progress>\\S*)%"); - - public GardenSkillsWidget() { - super(TITLE, Formatting.YELLOW.getColorValue()); - } - - @Override - public void updateContent() { - ProgressComponent pc; - Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN); - if (m == null) { - pc = new ProgressComponent(); - } else { - - String strpcnt = m.group("progress"); - String skill = m.group("skill"); - - float pcnt = Float.parseFloat(strpcnt); - pc = new ProgressComponent(Ico.LANTERN, Text.of(skill), pcnt, - Formatting.GOLD.getColorValue()); - } - - this.addComponent(pc); - - Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE); - IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); - Text farmfort = Widget.simpleEntryText(68, "FFO", Formatting.GOLD); - IcoTextComponent ffo = new IcoTextComponent(Ico.HOE, farmfort); - - TableComponent tc = new TableComponent(2, 1, Formatting.YELLOW.getColorValue()); - tc.addToCell(0, 0, spd); - tc.addToCell(1, 0, ffo); - this.addComponent(tc); - - ProgressComponent pc2; - m = PlayerListMgr.regexAt(69, MS_PATTERN); - if (m == null) { - pc2 = new ProgressComponent(); - } else { - String strpcnt = m.group("progress"); - String skill = m.group("skill"); - - float pcnt = Float.parseFloat(strpcnt); - pc2 = new ProgressComponent(Ico.MILESTONE, Text.of(skill), pcnt, - Formatting.GREEN.getColorValue()); - - } - this.addComponent(pc2); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java deleted file mode 100644 index 945fb17c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GardenVisitorsWidget.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class GardenVisitorsWidget extends Widget { - private static final MutableText TITLE = Text.literal("Visitors").formatted(Formatting.DARK_GREEN, Formatting.BOLD); - - public GardenVisitorsWidget() { - super(TITLE, Formatting.DARK_GREEN.getColorValue()); - } - - @Override - public void updateContent() { - if (PlayerListMgr.textAt(54) == null) { - this.addComponent(new PlainTextComponent(Text.literal("No visitors!").formatted(Formatting.GRAY))); - return; - } - - for (int i = 54; i < 59; i++) { - String text = PlayerListMgr.strAt(i); - if (text != null) - this.addComponent(new PlainTextComponent(Text.literal(text))); - } - - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java deleted file mode 100644 index 6f1f4811..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/GuestServerWidget.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about the private island you're visiting - -public class GuestServerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public GuestServerWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); - this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); - this.addSimpleIcoText(Ico.SIGN, "Owner:", Formatting.GREEN, 43); - this.addSimpleIcoText(Ico.SIGN, "Status:", Formatting.BLUE, 44); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java deleted file mode 100644 index faf231a8..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandGuestsWidget.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows a list of all people visiting the same private island as you - -public class IslandGuestsWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Guests").formatted(Formatting.AQUA, - Formatting.BOLD); - - // matches a player entry, removing their level and the hand icon - // group 1: player name - private static final Pattern GUEST_PATTERN = Pattern.compile("\\[\\d*\\] (.*) \\[.\\]"); - - public IslandGuestsWidget() { - super(TITLE, Formatting.AQUA.getColorValue()); - } - - @Override - public void updateContent() { - for (int i = 21; i < 40; i++) { - String str = PlayerListMgr.strAt(i); - if (str == null) { - if (i == 21) { - this.addComponent(new PlainTextComponent(Text.literal("No Visitors!").formatted(Formatting.GRAY))); - } - break; - } - Matcher m = PlayerListMgr.regexAt( i, GUEST_PATTERN); - if (m == null) { - this.addComponent(new PlainTextComponent(Text.of("???"))); - } else { - this.addComponent(new PlainTextComponent(Text.of(m.group(1)))); - } - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java deleted file mode 100644 index afa883be..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandOwnersWidget.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows a list of the owners of a home island while guesting - -public class IslandOwnersWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - // matches an owner - // group 1: player name - // group 2: last seen, if owner not online - // ^(?<nameA>.*) \((?<lastseen>.*)\)$|^\[\d*\] (?:\[[A-Za-z]+\] )?(?<nameB>[A-Za-z0-9_]*)(?: .*)?$|^(?<nameC>.*)$ - private static final Pattern OWNER_PATTERN = Pattern - .compile("^(?<nameA>.*) \\((?<lastseen>.*)\\)$|^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?(?<nameB>[A-Za-z0-9_]*)(?: .*)?$|^(?<nameC>.*)$"); - - public IslandOwnersWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - - for (int i = 1; i < 20; i++) { - Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN); - if (m == null) { - break; - } - - String name, lastseen; - Formatting format; - if (m.group("nameA") != null) { - name = m.group("nameA"); - lastseen = m.group("lastseen"); - format = Formatting.GRAY; - } else if (m.group("nameB")!=null){ - name = m.group("nameB"); - lastseen = "Online"; - format = Formatting.WHITE; - } else { - name = m.group("nameC"); - lastseen = "Online"; - format = Formatting.WHITE; - } - - Text entry = Text.literal(name) - .append( - Text.literal(" (" + lastseen + ")") - .formatted(format)); - PlainTextComponent ptc = new PlainTextComponent(entry); - this.addComponent(ptc); - } - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java deleted file mode 100644 index cc7a2f0c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandSelfWidget.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows a list of the owners while on your home island - -public class IslandSelfWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Owners").formatted(Formatting.DARK_PURPLE, - Formatting.BOLD); - - // matches an owner - // group 1: player name, optionally offline time - // ^\[\d*\] (?:\[[A-Za-z]+\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$ - private static final Pattern OWNER_PATTERN = Pattern - .compile("^\\[\\d*\\] (?:\\[[A-Za-z]+\\] )?([A-Za-z0-9_() ]*)(?: .*)?$|^(.*)$"); - - public IslandSelfWidget() { - super(TITLE, Formatting.DARK_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - for (int i = 1; i < 20; i++) { - Matcher m = PlayerListMgr.regexAt(i, OWNER_PATTERN); - if (m == null) { - break; - } - - Text entry = (m.group(1) != null) ? Text.of(m.group(1)) : Text.of(m.group(2)); - this.addComponent(new PlainTextComponent(entry)); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java deleted file mode 100644 index 1ed15f5e..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/IslandServerWidget.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about your home island - -public class IslandServerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Island Info").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public IslandServerWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); - this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); - this.addSimpleIcoText(Ico.EMERALD, "Crystals:", Formatting.DARK_PURPLE, 43); - this.addSimpleIcoText(Ico.CHEST, "Stash:", Formatting.GREEN, 44); - this.addSimpleIcoText(Ico.COMMAND, "Minions:", Formatting.BLUE, 45); - - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java deleted file mode 100644 index 888c3697..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/JacobsContestWidget.java +++ /dev/null @@ -1,62 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.HashMap; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about the current jacob's contest (garden only) - -public class JacobsContestWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Jacob's Contest").formatted(Formatting.YELLOW, - Formatting.BOLD); - - private static final HashMap<String, ItemStack> FARM_DATA = new HashMap<>(); - - // again, there HAS to be a better way to do this - static { - FARM_DATA.put("Wheat", new ItemStack(Items.WHEAT)); - FARM_DATA.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); - FARM_DATA.put("Carrot", new ItemStack(Items.CARROT)); - FARM_DATA.put("Potato", new ItemStack(Items.POTATO)); - FARM_DATA.put("Melon", new ItemStack(Items.MELON_SLICE)); - FARM_DATA.put("Pumpkin", new ItemStack(Items.PUMPKIN)); - FARM_DATA.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); - FARM_DATA.put("Nether Wart", new ItemStack(Items.NETHER_WART)); - FARM_DATA.put("Cactus", new ItemStack(Items.CACTUS)); - FARM_DATA.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); - } - - public JacobsContestWidget() { - super(TITLE, Formatting.YELLOW.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.CLOCK, "Starts in:", Formatting.GOLD, 76); - - TableComponent tc = new TableComponent(1, 3, Formatting.YELLOW .getColorValue()); - - for (int i = 77; i < 80; i++) { - String item = PlayerListMgr.strAt(i); - IcoTextComponent itc; - if (item == null) { - itc = new IcoTextComponent(); - } else { - itc = new IcoTextComponent(FARM_DATA.get(item), Text.of(item)); - } - tc.addToCell(0, i - 77, itc); - } - this.addComponent(tc); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java deleted file mode 100644 index 579828d4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/MinionWidget.java +++ /dev/null @@ -1,151 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.HashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; - -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about minions placed on the home island - -public class MinionWidget extends Widget { - private static final MutableText TITLE = Text.literal("Minions").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - private static final HashMap<String, ItemStack> MIN_ICOS = new HashMap<>(); - - // hmm... - static { - MIN_ICOS.put("Blaze", new ItemStack(Items.BLAZE_ROD)); - MIN_ICOS.put("Cave Spider", new ItemStack(Items.SPIDER_EYE)); - MIN_ICOS.put("Creeper", new ItemStack(Items.GUNPOWDER)); - MIN_ICOS.put("Enderman", new ItemStack(Items.ENDER_PEARL)); - MIN_ICOS.put("Ghast", new ItemStack(Items.GHAST_TEAR)); - MIN_ICOS.put("Magma Cube", new ItemStack(Items.MAGMA_CREAM)); - MIN_ICOS.put("Skeleton", new ItemStack(Items.BONE)); - MIN_ICOS.put("Slime", new ItemStack(Items.SLIME_BALL)); - MIN_ICOS.put("Spider", new ItemStack(Items.STRING)); - MIN_ICOS.put("Zombie", new ItemStack(Items.ROTTEN_FLESH)); - MIN_ICOS.put("Cactus", new ItemStack(Items.CACTUS)); - MIN_ICOS.put("Carrot", new ItemStack(Items.CARROT)); - MIN_ICOS.put("Chicken", new ItemStack(Items.CHICKEN)); - MIN_ICOS.put("Cocoa Beans", new ItemStack(Items.COCOA_BEANS)); - MIN_ICOS.put("Cow", new ItemStack(Items.BEEF)); - MIN_ICOS.put("Melon", new ItemStack(Items.MELON_SLICE)); - MIN_ICOS.put("Mushroom", new ItemStack(Items.RED_MUSHROOM)); - MIN_ICOS.put("Nether Wart", new ItemStack(Items.NETHER_WART)); - MIN_ICOS.put("Pig", new ItemStack(Items.PORKCHOP)); - MIN_ICOS.put("Potato", new ItemStack(Items.POTATO)); - MIN_ICOS.put("Pumpkin", new ItemStack(Items.PUMPKIN)); - MIN_ICOS.put("Rabbit", new ItemStack(Items.RABBIT)); - MIN_ICOS.put("Sheep", new ItemStack(Items.WHITE_WOOL)); - MIN_ICOS.put("Sugar Cane", new ItemStack(Items.SUGAR_CANE)); - MIN_ICOS.put("Wheat", new ItemStack(Items.WHEAT)); - MIN_ICOS.put("Clay", new ItemStack(Items.CLAY)); - MIN_ICOS.put("Fishing", new ItemStack(Items.FISHING_ROD)); - MIN_ICOS.put("Coal", new ItemStack(Items.COAL)); - MIN_ICOS.put("Cobblestone", new ItemStack(Items.COBBLESTONE)); - MIN_ICOS.put("Diamond", new ItemStack(Items.DIAMOND)); - MIN_ICOS.put("Emerald", new ItemStack(Items.EMERALD)); - MIN_ICOS.put("End Stone", new ItemStack(Items.END_STONE)); - MIN_ICOS.put("Glowstone", new ItemStack(Items.GLOWSTONE_DUST)); - MIN_ICOS.put("Gold", new ItemStack(Items.GOLD_INGOT)); - MIN_ICOS.put("Gravel", new ItemStack(Items.GRAVEL)); - MIN_ICOS.put("Hard Stone", new ItemStack(Items.STONE)); - MIN_ICOS.put("Ice", new ItemStack(Items.ICE)); - MIN_ICOS.put("Iron", new ItemStack(Items.IRON_INGOT)); - MIN_ICOS.put("Lapis", new ItemStack(Items.LAPIS_LAZULI)); - MIN_ICOS.put("Mithril", new ItemStack(Items.PRISMARINE_CRYSTALS)); - MIN_ICOS.put("Mycelium", new ItemStack(Items.MYCELIUM)); - MIN_ICOS.put("Obsidian", new ItemStack(Items.OBSIDIAN)); - MIN_ICOS.put("Quartz", new ItemStack(Items.QUARTZ)); - MIN_ICOS.put("Red Sand", new ItemStack(Items.RED_SAND)); - MIN_ICOS.put("Redstone", new ItemStack(Items.REDSTONE)); - MIN_ICOS.put("Sand", new ItemStack(Items.SAND)); - MIN_ICOS.put("Snow", new ItemStack(Items.SNOWBALL)); - MIN_ICOS.put("Inferno", new ItemStack(Items.BLAZE_SPAWN_EGG)); - MIN_ICOS.put("Revenant", new ItemStack(Items.ZOMBIE_SPAWN_EGG)); - MIN_ICOS.put("Tarantula", new ItemStack(Items.SPIDER_SPAWN_EGG)); - MIN_ICOS.put("Vampire", new ItemStack(Items.REDSTONE)); - MIN_ICOS.put("Voidling", new ItemStack(Items.ENDERMAN_SPAWN_EGG)); - MIN_ICOS.put("Acacia", new ItemStack(Items.ACACIA_LOG)); - MIN_ICOS.put("Birch", new ItemStack(Items.BIRCH_LOG)); - MIN_ICOS.put("Dark Oak", new ItemStack(Items.DARK_OAK_LOG)); - MIN_ICOS.put("Flower", new ItemStack(Items.POPPY)); - MIN_ICOS.put("Jungle", new ItemStack(Items.JUNGLE_LOG)); - MIN_ICOS.put("Oak", new ItemStack(Items.OAK_LOG)); - MIN_ICOS.put("Spruce", new ItemStack(Items.SPRUCE_LOG)); - } - - // matches a minion entry - // group 1: name - // group 2: level - // group 3: status - public static final Pattern MINION_PATTERN = Pattern.compile("(?<name>.*) (?<level>[XVI]*) \\[(?<status>.*)\\]"); - - public MinionWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - - // this looks a bit weird because if we used regex mismatch as a stop condition, - // it'd spam the chat. - // not sure if not having that debug output is worth the cleaner solution here... - - for (int i = 48; i < 59; i++) { - if (!this.addMinionComponent(i)) { - break; - } - } - - // if more minions are placed than the tab menu can display, - // a "And X more..." text is shown - // look for that and add it to the widget - String more = PlayerListMgr.strAt(59); - if (more == null) { - return; - } else if (more.startsWith("And ")) { - this.addComponent(new PlainTextComponent(Text.of(more))); - } else { - this.addMinionComponent(59); - } - } - - public boolean addMinionComponent(int i) { - Matcher m = PlayerListMgr.regexAt(i, MINION_PATTERN); - if (m != null) { - - String min = m.group("name"); - String lvl = m.group("level"); - String stat = m.group("status"); - - MutableText mt = Text.literal(min + " " + lvl).append(Text.literal(": ")); - - Formatting format = Formatting.RED; - if (stat.equals("ACTIVE")) { - format = Formatting.GREEN; - } else if (stat.equals("SLOW")) { - format = Formatting.YELLOW; - } - // makes "BLOCKED" also red. in reality, it's some kind of crimson - mt.append(Text.literal(stat).formatted(format)); - - IcoTextComponent itc = new IcoTextComponent(MIN_ICOS.get(min), mt); - this.addComponent(itc); - return true; - } else { - return false; - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java deleted file mode 100644 index 2c422bd1..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ParkServerWidget.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about the park server - -public class ParkServerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public ParkServerWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); - this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); - this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); - this.addSimpleIcoText(Ico.WATER, "Rain:", Formatting.BLUE, 44); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java deleted file mode 100644 index 241cb2a2..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PlayerListWidget.java +++ /dev/null @@ -1,71 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlayerComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent; -import net.minecraft.client.network.PlayerListEntry; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.ArrayList; -import java.util.Comparator; - -// this widget shows a list of players with their skins. -// responsible for non-private-island areas - -public class PlayerListWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Players").formatted(Formatting.GREEN, - Formatting.BOLD); - - public PlayerListWidget() { - super(TITLE, Formatting.GREEN.getColorValue()); - - } - - @Override - public void updateContent() { - ArrayList<PlayerListEntry> list = new ArrayList<>(); - - // hard cap to 4x20 entries. - // 5x20 is too wide (and not possible in theory. in reality however...) - int listlen = Math.min(PlayerListMgr.getSize(), 160); - - // list isn't fully loaded, so our hack won't work... - if (listlen < 80) { - this.addComponent(new PlainTextComponent(Text.literal("List loading...").formatted(Formatting.GRAY))); - return; - } - - // unintuitive int ceil division stolen from - // https://stackoverflow.com/questions/7139382/java-rounding-up-to-an-int-using-math-ceil#21830188 - int tblW = ((listlen - 80) - 1) / 20 + 1; - - TableComponent tc = new TableComponent(tblW, Math.min(listlen - 80, 20), Formatting.GREEN.getColorValue()); - - for (int i = 80; i < listlen; i++) { - list.add(PlayerListMgr.getRaw(i)); - } - - if (SkyblockerConfigManager.get().general.tabHud.nameSorting == SkyblockerConfig.NameSorting.ALPHABETICAL) { - list.sort(Comparator.comparing(o -> o.getProfile().getName().toLowerCase())); - } - - int x = 0, y = 0; - - for (PlayerListEntry ple : list) { - tc.addToCell(x, y, new PlayerComponent(ple)); - y++; - if (y >= 20) { - y = 0; - x++; - } - } - - this.addComponent(tc); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java deleted file mode 100644 index 7e56d4d9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/PowderWidget.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows how much mithril and gemstone powder you have -// (dwarven mines and crystal hollows) - -public class PowderWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Powders").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public PowderWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MITHRIL, "Mithril:", Formatting.AQUA, 46); - this.addSimpleIcoText(Ico.EMERALD, "Gemstone:", Formatting.DARK_PURPLE, 47); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java deleted file mode 100644 index 8bc94622..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ProfileWidget.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about your profile and bank - -public class ProfileWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.YELLOW, Formatting.BOLD); - - public ProfileWidget() { - super(TITLE, Formatting.YELLOW.getColorValue()); - - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61); - this.addSimpleIcoText(Ico.BONE, "Pet Sitter:", Formatting.AQUA, 62); - this.addSimpleIcoText(Ico.EMERALD, "Balance:", Formatting.GOLD, 63); - this.addSimpleIcoText(Ico.CLOCK, "Interest in:", Formatting.GOLD, 64); - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java deleted file mode 100644 index 02137b1a..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/QuestWidget.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows your crimson isle faction quests - -public class QuestWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Faction Quests").formatted(Formatting.AQUA, - Formatting.BOLD); - - public QuestWidget() { - super(TITLE, Formatting.AQUA.getColorValue()); - - } - - @Override - public void updateContent() { - for (int i = 51; i < 56; i++) { - Text q = PlayerListMgr.textAt(i); - IcoTextComponent itc = new IcoTextComponent(Ico.BOOK, q); - this.addComponent(itc); - } - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java deleted file mode 100644 index 32060bd0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ReputationWidget.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows your faction status (crimson isle) - -public class ReputationWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Faction Status").formatted(Formatting.AQUA, - Formatting.BOLD); - - // matches your faction alignment progress - // group 1: percentage to next alignment level - private static final Pattern PROGRESS_PATTERN = Pattern.compile("\\|+ \\((?<prog>[0-9.]*)%\\)"); - - // matches alignment level names - // group 1: left level name - // group 2: right level name - private static final Pattern STATE_PATTERN = Pattern.compile("(?<from>\\S*) *(?<to>\\S*)"); - - public ReputationWidget() { - super(TITLE, Formatting.AQUA.getColorValue()); - } - - @Override - public void updateContent() { - String fracstr = PlayerListMgr.strAt(45); - - int spaceidx; - IcoTextComponent faction; - if (fracstr == null || (spaceidx = fracstr.indexOf(' ')) == -1) { - faction = new IcoTextComponent(); - } else { - String fname = fracstr.substring(0, spaceidx); - if (fname.equals("Mage")) { - faction = new IcoTextComponent(Ico.POTION, Text.literal(fname).formatted(Formatting.DARK_AQUA)); - } else { - faction = new IcoTextComponent(Ico.SWORD, Text.literal(fname).formatted(Formatting.RED)); - } - } - this.addComponent(faction); - - Text rep = Widget.plainEntryText(46); - Matcher prog = PlayerListMgr.regexAt(47, PROGRESS_PATTERN); - Matcher state = PlayerListMgr.regexAt(48, STATE_PATTERN); - - if (prog == null || state == null) { - this.addComponent(new ProgressComponent()); - } else { - float pcnt = Float.parseFloat(prog.group("prog")); - Text reputationText = state.group("from").equals("Max") ? Text.literal("Max Reputation") : Text.literal(state.group("from") + " -> " + state.group("to")); - ProgressComponent pc = new ProgressComponent(Ico.LANTERN, - reputationText, rep, pcnt, - Formatting.AQUA.getColorValue()); - this.addComponent(pc); - } - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java deleted file mode 100644 index 62c01b63..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/ServerWidget.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about "generic" servers. -// a server is "generic", when only name, server ID and gems are shown -// in the third column of the tab HUD - -public class ServerWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public ServerWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41); - this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); - this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java deleted file mode 100644 index cecee76c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/SkillsWidget.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoFatTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about a skill and some stats, -// as seen in the rightmost column of the default HUD - -public class SkillsWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Skill Info").formatted(Formatting.YELLOW, - Formatting.BOLD); - - // match the skill entry - // group 1: skill name and level - // group 2: progress to next level (without "%") - private static final Pattern SKILL_PATTERN = Pattern.compile("\\S*: ([A-Za-z]* [0-9]*): ([0-9.MAX]*)%?"); - - public SkillsWidget() { - super(TITLE, Formatting.YELLOW.getColorValue()); - - } - - @Override - public void updateContent() { - Matcher m = PlayerListMgr.regexAt(66, SKILL_PATTERN); - Component progress; - if (m == null) { - progress = new ProgressComponent(); - } else { - - String skill = m.group(1); - String pcntStr = m.group(2); - - if (!pcntStr.equals("MAX")) { - float pcnt = Float.parseFloat(pcntStr); - progress = new ProgressComponent(Ico.LANTERN, Text.of(skill), - Text.of(pcntStr + "%"), pcnt, Formatting.GOLD.getColorValue()); - } else { - progress = new IcoFatTextComponent(Ico.LANTERN, Text.of(skill), - Text.literal(pcntStr).formatted(Formatting.RED)); - } - } - - this.addComponent(progress); - - Text speed = Widget.simpleEntryText(67, "SPD", Formatting.WHITE); - IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); - Text strength = Widget.simpleEntryText(68, "STR", Formatting.RED); - IcoTextComponent str = new IcoTextComponent(Ico.SWORD, strength); - Text critDmg = Widget.simpleEntryText(69, "CCH", Formatting.BLUE); - IcoTextComponent cdg = new IcoTextComponent(Ico.SWORD, critDmg); - Text critCh = Widget.simpleEntryText(70, "CDG", Formatting.BLUE); - IcoTextComponent cch = new IcoTextComponent(Ico.SWORD, critCh); - Text aSpeed = Widget.simpleEntryText(71, "ASP", Formatting.YELLOW); - IcoTextComponent asp = new IcoTextComponent(Ico.HOE, aSpeed); - - TableComponent tc = new TableComponent(2, 3, Formatting.YELLOW.getColorValue()); - tc.addToCell(0, 0, spd); - tc.addToCell(0, 1, str); - tc.addToCell(0, 2, asp); - tc.addToCell(1, 0, cdg); - tc.addToCell(1, 1, cch); - this.addComponent(tc); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java deleted file mode 100644 index 68f8dcf2..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/TrapperWidget.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows how meny pelts you have (farming island) - -public class TrapperWidget extends Widget { - private static final MutableText TITLE = Text.literal("Trapper").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public TrapperWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.LEATHER, "Pelts:", Formatting.AQUA, 46); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java deleted file mode 100644 index 4553c7f9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/UpgradeWidget.java +++ /dev/null @@ -1,51 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -// this widget shows info about ongoing profile/account upgrades -// or not, if there aren't any -// TODO: not very pretty atm - -public class UpgradeWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Upgrade Info").formatted(Formatting.GOLD, - Formatting.BOLD); - - public UpgradeWidget() { - super(TITLE, Formatting.GOLD.getColorValue()); - } - - @Override - public void updateContent() { - String footertext = PlayerListMgr.getFooter(); - - if (footertext == null) { - this.addComponent(new PlainTextComponent(Text.literal("No data").formatted(Formatting.GRAY))); - return; - } - - if (!footertext.contains("Upgrades")) { - this.addComponent(new PlainTextComponent(Text.of("Currently no upgrades..."))); - return; - } - - String interesting = footertext.split("Upgrades")[1]; - String[] lines = interesting.split("\n"); - - for (int i = 1; i < lines.length; i++) { - if (lines[i].trim().length() < 3) { // empty line is §s - break; - } - IcoTextComponent itc = new IcoTextComponent(Ico.SIGN, Text.of(lines[i])); - this.addComponent(itc); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java deleted file mode 100644 index 90f947ba..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/VolcanoWidget.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.HashMap; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Pair; - -// shows the volcano status (crimson isle) - -public class VolcanoWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Volcano Status").formatted(Formatting.AQUA, - Formatting.BOLD); - - private static final HashMap<String, Pair<ItemStack, Formatting>> BOOM_TYPE = new HashMap<>(); - - static { - BOOM_TYPE.put("INACTIVE", - new Pair<>(new ItemStack(Items.BARRIER), Formatting.DARK_GRAY)); - BOOM_TYPE.put("CHILL", - new Pair<>(new ItemStack(Items.ICE), Formatting.AQUA)); - BOOM_TYPE.put("LOW", - new Pair<>(new ItemStack(Items.FLINT_AND_STEEL), Formatting.GRAY)); - BOOM_TYPE.put("DISRUPTIVE", - new Pair<>(new ItemStack(Items.CAMPFIRE), Formatting.WHITE)); - BOOM_TYPE.put("MEDIUM", - new Pair<>(new ItemStack(Items.LAVA_BUCKET), Formatting.YELLOW)); - BOOM_TYPE.put("HIGH", - new Pair<>(new ItemStack(Items.FIRE_CHARGE), Formatting.GOLD)); - BOOM_TYPE.put("EXPLOSIVE", - new Pair<>(new ItemStack(Items.TNT), Formatting.RED)); - BOOM_TYPE.put("CATACLYSMIC", - new Pair<>(new ItemStack(Items.SKELETON_SKULL), Formatting.DARK_RED)); - } - - public VolcanoWidget() { - super(TITLE, Formatting.AQUA.getColorValue()); - - } - - @Override - public void updateContent() { - String s = PlayerListMgr.strAt(58); - if (s == null) { - this.addComponent(new IcoTextComponent()); - } else { - Pair<ItemStack, Formatting> p = BOOM_TYPE.get(s); - this.addComponent(new IcoTextComponent(p.getLeft(), Text.literal(s).formatted(p.getRight()))); - } - - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java deleted file mode 100644 index 97d31981..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/Widget.java +++ /dev/null @@ -1,216 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget; - -import java.util.ArrayList; - -import com.mojang.blaze3d.systems.RenderSystem; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -/** - * Abstract base class for a Widget. - * Widgets are containers for components with a border and a title. - * Their size is dependent on the components inside, - * the position may be changed after construction. - */ -public abstract class Widget { - - private final ArrayList<Component> components = new ArrayList<>(); - private int w = 0, h = 0; - private int x = 0, y = 0; - private final int color; - private final Text title; - - private static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer; - - static final int BORDER_SZE_N = txtRend.fontHeight + 4; - static final int BORDER_SZE_S = 4; - static final int BORDER_SZE_W = 4; - static final int BORDER_SZE_E = 4; - static final int COL_BG_BOX = 0xc00c0c0c; - - public Widget(MutableText title, Integer colorValue) { - this.title = title; - this.color = 0xff000000 | colorValue; - } - - public final void addComponent(Component c) { - this.components.add(c); - } - - public final void update() { - this.components.clear(); - this.updateContent(); - this.pack(); - } - - public abstract void updateContent(); - - /** - * Shorthand function for simple components. - * If the entry at idx has the format "<textA>: <textB>", an IcoTextComponent is - * added as such: - * [ico] [string] [textB.formatted(fmt)] - */ - public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, int idx) { - Text txt = Widget.simpleEntryText(idx, string, fmt); - this.addComponent(new IcoTextComponent(ico, txt)); - } - - /** - * Calculate the size of this widget. - * <b>Must be called before returning from the widget constructor and after all - * components are added!</b> - */ - private void pack() { - h = 0; - w = 0; - for (Component c : components) { - h += c.getHeight() + Component.PAD_L; - w = Math.max(w, c.getWidth() + Component.PAD_S); - } - - h -= Component.PAD_L / 2; // less padding after lowest/last component - h += BORDER_SZE_N + BORDER_SZE_S - 2; - w += BORDER_SZE_E + BORDER_SZE_W; - - // min width is dependent on title - w = Math.max(w, BORDER_SZE_W + BORDER_SZE_E + Widget.txtRend.getWidth(title) + 4 + 4 + 1); - } - - public final void setX(int x) { - this.x = x; - } - - public final int getY() { - return this.y; - } - - public final int getX() { - return this.x; - } - - public final void setY(int y) { - this.y = y; - } - - public final int getWidth() { - return this.w; - } - - public final int getHeight() { - return this.h; - } - - /** - * Draw this widget with a background - */ - public final void render(DrawContext context) { - this.render(context, true); - } - - /** - * Draw this widget, possibly with a background - */ - public final void render(DrawContext context, boolean hasBG) { - MatrixStack ms = context.getMatrices(); - - // not sure if this is the way to go, but it fixes Z-layer issues - // like blocks being rendered behind the BG and the hotbar clipping into things - RenderSystem.enableDepthTest(); - ms.push(); - - float scale = SkyblockerConfigManager.get().general.tabHud.tabHudScale / 100f; - ms.scale(scale, scale, 1); - - // move above other UI elements - ms.translate(0, 0, 200); - if (hasBG) { - context.fill(x + 1, y, x + w - 1, y + h, COL_BG_BOX); - context.fill(x, y + 1, x + 1, y + h - 1, COL_BG_BOX); - context.fill(x + w - 1, y + 1, x + w, y + h - 1, COL_BG_BOX); - } - // move above background (if exists) - ms.translate(0, 0, 100); - - int strHeightHalf = Widget.txtRend.fontHeight / 2; - int strAreaWidth = Widget.txtRend.getWidth(title) + 4; - - context.drawText(txtRend, title, x + 8, y + 2, this.color, false); - - this.drawHLine(context, x + 2, y + 1 + strHeightHalf, 4); - this.drawHLine(context, x + 2 + strAreaWidth + 4, y + 1 + strHeightHalf, w - 4 - 4 - strAreaWidth); - this.drawHLine(context, x + 2, y + h - 2, w - 4); - - this.drawVLine(context, x + 1, y + 2 + strHeightHalf, h - 4 - strHeightHalf); - this.drawVLine(context, x + w - 2, y + 2 + strHeightHalf, h - 4 - strHeightHalf); - - int yOffs = y + BORDER_SZE_N; - - for (Component c : components) { - c.render(context, x + BORDER_SZE_W, yOffs); - yOffs += c.getHeight() + Component.PAD_L; - } - // pop manipulations above - ms.pop(); - RenderSystem.disableDepthTest(); - } - - private void drawHLine(DrawContext context, int xpos, int ypos, int width) { - context.fill(xpos, ypos, xpos + width, ypos + 1, this.color); - } - - private void drawVLine(DrawContext context, int xpos, int ypos, int height) { - context.fill(xpos, ypos, xpos + 1, ypos + height, this.color); - } - - /** - * If the entry at idx has the format "[textA]: [textB]", the following is - * returned: - * [entryName] [textB.formatted(contentFmt)] - */ - public static Text simpleEntryText(int idx, String entryName, Formatting contentFmt) { - - String src = PlayerListMgr.strAt(idx); - - if (src == null) { - return null; - } - - int cidx = src.indexOf(':'); - if (cidx == -1) { - return null; - } - - src = src.substring(src.indexOf(':') + 1); - return Widget.simpleEntryText(src, entryName, contentFmt); - } - - /** - * @return [entryName] [entryContent.formatted(contentFmt)] - */ - public static Text simpleEntryText(String entryContent, String entryName, Formatting contentFmt) { - return Text.literal(entryName).append(Text.literal(entryContent).formatted(contentFmt)); - } - - /** - * @return the entry at idx as unformatted Text - */ - public static Text plainEntryText(int idx) { - String str = PlayerListMgr.strAt(idx); - if (str == null) { - return null; - } - return Text.of(str); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java deleted file mode 100644 index 118d3cfe..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/Component.java +++ /dev/null @@ -1,31 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; - -/** - * Abstract base class for a component that may be added to a Widget. - */ -public abstract class Component { - - static final int ICO_DIM = 16; - public static final int PAD_S = 2; - public static final int PAD_L = 4; - - static final TextRenderer txtRend = MinecraftClient.getInstance().textRenderer; - - // these should always be the content dimensions without any padding. - int width, height; - - public abstract void render(DrawContext context, int x, int y); - - public int getWidth() { - return this.width; - } - - public int getHeight() { - return this.height; - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java deleted file mode 100644 index afd05c26..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoFatTextComponent.java +++ /dev/null @@ -1,45 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -/** - * Component that consists of an icon and two lines of text - */ -public class IcoFatTextComponent extends Component { - - private static final int ICO_OFFS = 1; - - private ItemStack ico; - private Text line1, line2; - - public IcoFatTextComponent(ItemStack ico, Text l1, Text l2) { - this.ico = (ico == null) ? Ico.BARRIER : ico; - this.line1 = l1; - this.line2 = l2; - - if (l1 == null || l2 == null) { - this.ico = Ico.BARRIER; - this.line1 = Text.literal("No data").formatted(Formatting.GRAY); - this.line2 = Text.literal("No data").formatted(Formatting.GRAY); - } - - this.width = ICO_DIM + PAD_L + Math.max(txtRend.getWidth(this.line1), txtRend.getWidth(this.line2)); - this.height = txtRend.fontHeight + PAD_S + txtRend.fontHeight; - } - - public IcoFatTextComponent() { - this(null, null, null); - } - - @Override - public void render(DrawContext context, int x, int y) { - context.drawItem(ico, x, y + ICO_OFFS); - context.drawText(txtRend, line1, x + ICO_DIM + PAD_L, y, 0xffffffff, false); - context.drawText(txtRend, line2, x + ICO_DIM + PAD_L, y + txtRend.fontHeight + PAD_S, 0xffffffff, false); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java deleted file mode 100644 index 7ab92dd5..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/IcoTextComponent.java +++ /dev/null @@ -1,40 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -/** - * Component that consists of an icon and a line of text. - */ -public class IcoTextComponent extends Component { - - private ItemStack ico; - private Text text; - - public IcoTextComponent(ItemStack ico, Text txt) { - this.ico = (ico == null) ? Ico.BARRIER : ico; - this.text = txt; - - if (txt == null) { - this.ico = Ico.BARRIER; - this.text = Text.literal("No data").formatted(Formatting.GRAY); - } - - this.width = ICO_DIM + PAD_L + txtRend.getWidth(this.text); - this.height = ICO_DIM; - } - - public IcoTextComponent() { - this(null, null); - } - - @Override - public void render(DrawContext context, int x, int y) { - context.drawItem(ico, x, y); - context.drawText(txtRend, text, x + ICO_DIM + PAD_L, y + 5, 0xffffffff, false); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java deleted file mode 100644 index 34e0268b..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlainTextComponent.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import net.minecraft.client.gui.DrawContext; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -/** - * Component that consists of a line of text. - */ -public class PlainTextComponent extends Component { - - private Text text; - - public PlainTextComponent(Text txt) { - this.text = txt; - - if (txt == null) { - this.text = Text.literal("No data").formatted(Formatting.GRAY); - } - - this.width = PAD_S + txtRend.getWidth(this.text); // looks off without padding - this.height = txtRend.fontHeight; - } - - @Override - public void render(DrawContext context, int x, int y) { - context.drawText(txtRend, text, x + PAD_S, y, 0xffffffff, false); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java deleted file mode 100644 index cea8f6f0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/PlayerComponent.java +++ /dev/null @@ -1,39 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.PlayerSkinDrawer; -import net.minecraft.client.network.PlayerListEntry; -import net.minecraft.scoreboard.Team; -import net.minecraft.text.Text; -import net.minecraft.util.Identifier; - -/** - * Component that consists of a player's skin icon and their name - */ -public class PlayerComponent extends Component { - - private static final int SKIN_ICO_DIM = 8; - - private final Text name; - private final Identifier tex; - - public PlayerComponent(PlayerListEntry ple) { - - boolean plainNames = SkyblockerConfigManager.get().general.tabHud.plainPlayerNames; - Team team = ple.getScoreboardTeam(); - String username = ple.getProfile().getName(); - name = (team != null && !plainNames) ? Text.empty().append(team.getPrefix()).append(Text.literal(username).formatted(team.getColor())).append(team.getSuffix()) : Text.of(username); - tex = ple.getSkinTextures().texture(); - - this.width = SKIN_ICO_DIM + PAD_S + txtRend.getWidth(name); - this.height = txtRend.fontHeight; - } - - @Override - public void render(DrawContext context, int x, int y) { - PlayerSkinDrawer.draw(context, tex, x, y, SKIN_ICO_DIM); - context.drawText(txtRend, name, x + SKIN_ICO_DIM + PAD_S, y, 0xffffffff, false); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java deleted file mode 100644 index 90e210e9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/ProgressComponent.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.item.ItemStack; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - - -/** - * Component that consists of an icon, some text and a progress bar. - * The progress bar either shows the fill percentage or custom text. - * NOTICE: pcnt is 0-100, not 0-1! - */ -public class ProgressComponent extends Component { - - private static final int BAR_WIDTH = 100; - private static final int BAR_HEIGHT = txtRend.fontHeight + 3; - private static final int ICO_OFFS = 4; - private static final int COL_BG_BAR = 0xf0101010; - - private final ItemStack ico; - private final Text desc, bar; - private final float pcnt; - private final int color; - private final int barW; - - public ProgressComponent(ItemStack ico, Text d, Text b, float pcnt, int color) { - if (d == null || b == null) { - this.ico = Ico.BARRIER; - this.desc = Text.literal("No data").formatted(Formatting.GRAY); - this.bar = Text.literal("---").formatted(Formatting.GRAY); - this.pcnt = 100f; - this.color = 0xff000000 | Formatting.DARK_GRAY.getColorValue(); - } else { - this.ico = (ico == null) ? Ico.BARRIER : ico; - this.desc = d; - this.bar = b; - this.pcnt = pcnt; - this.color = 0xff000000 | color; - } - - this.barW = BAR_WIDTH; - this.width = ICO_DIM + PAD_L + Math.max(this.barW, txtRend.getWidth(this.desc)); - this.height = txtRend.fontHeight + PAD_S + 2 + txtRend.fontHeight + 2; - } - - public ProgressComponent(ItemStack ico, Text text, float pcnt, int color) { - this(ico, text, Text.of(pcnt + "%"), pcnt, color); - } - - public ProgressComponent() { - this(null, null, null, 100, 0); - } - - @Override - public void render(DrawContext context, int x, int y) { - context.drawItem(ico, x, y + ICO_OFFS); - context.drawText(txtRend, desc, x + ICO_DIM + PAD_L, y, 0xffffffff, false); - - int barX = x + ICO_DIM + PAD_L; - int barY = y + txtRend.fontHeight + PAD_S; - int endOffsX = ((int) (this.barW * (this.pcnt / 100f))); - context.fill(barX + endOffsX, barY, barX + this.barW, barY + BAR_HEIGHT, COL_BG_BAR); - context.fill(barX, barY, barX + endOffsX, barY + BAR_HEIGHT, - this.color); - context.drawTextWithShadow(txtRend, bar, barX + 3, barY + 2, 0xffffffff); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java deleted file mode 100644 index 850bbb0d..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/component/TableComponent.java +++ /dev/null @@ -1,58 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component; - -import net.minecraft.client.gui.DrawContext; - -/** - * Meta-Component that consists of a grid of other components - * Grid cols are separated by lines. - */ -public class TableComponent extends Component { - - private final Component[][] comps; - private final int color; - private final int cols, rows; - private int cellW, cellH; - - public TableComponent(int w, int h, int col) { - comps = new Component[w][h]; - color = 0xff000000 | col; - cols = w; - rows = h; - } - - public void addToCell(int x, int y, Component c) { - this.comps[x][y] = c; - - // pad extra to add a vertical line later - this.cellW = Math.max(this.cellW, c.width + PAD_S + PAD_L); - - // assume all rows are equally high so overwriting doesn't matter - // if this wasn't the case, drawing would need more math - // not doing any of that if it's not needed - this.cellH = c.height + PAD_S; - - this.width = this.cellW * this.cols; - this.height = (this.cellH * this.rows) - PAD_S / 2; - - } - - @Override - public void render(DrawContext context, int xpos, int ypos) { - for (int x = 0; x < cols; x++) { - for (int y = 0; y < rows; y++) { - if (comps[x][y] != null) { - comps[x][y].render(context, xpos + (x * cellW), ypos + y * cellH); - } - } - // add a line before the col if we're not drawing the first one - if (x != 0) { - int lineX1 = xpos + (x * cellW) - PAD_S - 1; - int lineX2 = xpos + (x * cellW) - PAD_S; - int lineY1 = ypos + 1; - int lineY2 = ypos + this.height - PAD_S - 1; // not sure why but it looks correct - context.fill(lineX1, lineY1, lineX2, lineY2, this.color); - } - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java deleted file mode 100644 index 88d40891..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/hud/HudCommsWidget.java +++ /dev/null @@ -1,73 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.hud; - -import java.util.List; - -import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud.Commission; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.Component; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.MathHelper; - -// this widget shows the status of the king's commissions. -// (dwarven mines and crystal hollows) -// USE ONLY WITH THE DWARVEN HUD! - -public class HudCommsWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Commissions").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - private List<Commission> commissions; - private boolean isFancy; - - // disgusting hack to get around text renderer issues. - // the ctor eventually tries to get the font's height, which doesn't work - // when called before the client window is created (roughly). - // the rebdering god 2 from the fabricord explained that detail, thanks! - public static final HudCommsWidget INSTANCE = new HudCommsWidget(); - public static final HudCommsWidget INSTANCE_CFG = new HudCommsWidget(); - - // another repulsive hack to make this widget-like hud element work with the new widget class - // DON'T USE WITH THE WIDGET SYSTEM, ONLY USE FOR DWARVENHUD! - public HudCommsWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - public void updateData(List<Commission> commissions, boolean isFancy) { - this.commissions = commissions; - this.isFancy = isFancy; - } - - @Override - public void updateContent() { - for (Commission comm : commissions) { - - Text c = Text.literal(comm.commission()); - - float p = 100f; - if (!comm.progression().contains("DONE")) { - p = Float.parseFloat(comm.progression().substring(0, comm.progression().length() - 1)); - } - - Component comp; - if (isFancy) { - comp = new ProgressComponent(Ico.BOOK, c, p, pcntToCol(p)); - } else { - comp = new PlainTextComponent( - Text.literal(comm.commission() + ": ") - .append(Text.literal(comm.progression()).formatted(Formatting.GREEN))); - } - this.addComponent(comp); - } - } - - private int pcntToCol(float pcnt) { - return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java deleted file mode 100644 index 8d50fc2f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/AdvertisementWidget.java +++ /dev/null @@ -1,35 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class AdvertisementWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Advertisement").formatted(Formatting.DARK_AQUA, - Formatting.BOLD); - - public AdvertisementWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - boolean added = false; - for (int i = 73; i < 80; i++) { - Text text = PlayerListMgr.textAt(i); - if (text != null) { - this.addComponent(new PlainTextComponent(text)); - added = true; - } - } - - if (!added) { - this.addComponent(new PlainTextComponent(Text.literal("No Advertisements").formatted(Formatting.GRAY))); - } - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java deleted file mode 100644 index d1a3df1f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/GoodToKnowWidget.java +++ /dev/null @@ -1,69 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class GoodToKnowWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Good To Know").formatted(Formatting.BLUE, Formatting.BOLD); - - public GoodToKnowWidget() { - super(TITLE, Formatting.BLUE.getColorValue()); - } - - @Override - public void updateContent() { - // After you progress further the tab adds more info so we need to be careful of - // that - // In beginning it only shows montezuma, then timecharms and enigma souls are - // added - - int headerPos = 0; - // this seems suboptimal, but I'm not sure if there's a way to do it better. - // search for the GTK header and offset the rest accordingly. - for (int i = 45; i <= 49; i++) { - String str = PlayerListMgr.strAt(i); - if (str != null && str.startsWith("Good to")) { - headerPos = i; - break; - } - } - - Text posA = PlayerListMgr.textAt(headerPos + 2); // Can be times visited rift - Text posB = PlayerListMgr.textAt(headerPos + 4); // Can be lifetime motes or visited rift - Text posC = PlayerListMgr.textAt(headerPos + 6); // Can be lifetime motes - - int visitedRiftPos = 0; - int lifetimeMotesPos = 0; - - // Check each position to see what is or isn't there so we don't try adding - // invalid components - if (posA != null && posA.getString().contains("times")) - visitedRiftPos = headerPos + 2; - if (posB != null && posB.getString().contains("Motes")) - lifetimeMotesPos = headerPos + 4; - if (posB != null && posB.getString().contains("times")) - visitedRiftPos = headerPos + 4; - if (posC != null && posC.getString().contains("Motes")) - lifetimeMotesPos = headerPos + 6; - - Text timesVisitedRift = (visitedRiftPos == headerPos + 4) ? posB : (visitedRiftPos == headerPos + 2) ? posA : Text.literal("No Data").formatted(Formatting.GRAY); - Text lifetimeMotesEarned = (lifetimeMotesPos == headerPos + 6) ? posC : (lifetimeMotesPos == headerPos + 4) ? posB : Text.literal("No Data").formatted(Formatting.GRAY); - - if (visitedRiftPos != 0) { - this.addComponent(new IcoTextComponent(Ico.EXPERIENCE_BOTTLE, - Text.literal("Visited Rift: ").append(timesVisitedRift))); - } - - if (lifetimeMotesPos != 0) { - this.addComponent( - new IcoTextComponent(Ico.PINK_DYE, Text.literal("Lifetime Earned: ").append(lifetimeMotesEarned))); - } - - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java deleted file mode 100644 index 785850d5..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProfileWidget.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class RiftProfileWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Profile").formatted(Formatting.DARK_AQUA, Formatting.BOLD); - - public RiftProfileWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.SIGN, "Profile:", Formatting.GREEN, 61); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java deleted file mode 100644 index ad43c9f4..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftProgressWidget.java +++ /dev/null @@ -1,123 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.ProgressComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.math.MathHelper; - -public class RiftProgressWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Rift Progress").formatted(Formatting.BLUE, Formatting.BOLD); - - private static final Pattern TIMECHARMS_PATTERN = Pattern.compile("Timecharms: (?<current>[0-9]+)\\/(?<total>[0-9]+)"); - private static final Pattern ENIGMA_SOULS_PATTERN = Pattern.compile("Enigma Souls: (?<current>[0-9]+)\\/(?<total>[0-9]+)"); - private static final Pattern MONTEZUMA_PATTERN = Pattern.compile("Montezuma: (?<current>[0-9]+)\\/(?<total>[0-9]+)"); - - public RiftProgressWidget() { - super(TITLE, Formatting.BLUE.getColorValue()); - } - - @Override - public void updateContent() { - // After you progress further, the tab adds more info so we need to be careful - // of that. - // In beginning it only shows montezuma, then timecharms and enigma souls are - // added. - - String pos44 = PlayerListMgr.strAt(44); - - // LHS short-circuits, so the RHS won't be evaluated on pos44 == null - if (pos44 == null || !pos44.contains("Rift Progress")) { - this.addComponent(new PlainTextComponent(Text.literal("No Progress").formatted(Formatting.GRAY))); - return; - } - - // let's try to be clever by assuming what progress item may appear where and - // when to skip testing every slot for every thing. - - // always non-null, as this holds the topmost item. - // if there is none, there shouldn't be a header. - String pos45 = PlayerListMgr.strAt(45); - - // Can be Montezuma, Enigma Souls or Timecharms. - // assume timecharms can only appear here and that they're the last thing to - // appear, so if this exists, we know the rest. - if (pos45.contains("Timecharms")) { - addTimecharmsComponent(45); - addEnigmaSoulsComponent(46); - addMontezumaComponent(47); - return; - } - - // timecharms didn't appear at the top, so there's two or one entries. - // assume that if there's two, souls is always top. - String pos46 = PlayerListMgr.strAt(46); - - if (pos45.contains("Enigma Souls")) { - addEnigmaSoulsComponent(45); - if (pos46 != null) { - // souls might appear alone. - // if there's a second entry, it has to be montezuma - addMontezumaComponent(46); - } - } else { - // first entry isn't souls, so it's just montezuma and nothing else. - addMontezumaComponent(45); - } - - } - - private static int pcntToCol(float pcnt) { - return MathHelper.hsvToRgb(pcnt / 300f, 0.9f, 0.9f); - } - - private void addTimecharmsComponent(int pos) { - Matcher m = PlayerListMgr.regexAt(pos, TIMECHARMS_PATTERN); - - int current = Integer.parseInt(m.group("current")); - int total = Integer.parseInt(m.group("total")); - float pcnt = ((float) current / (float) total) * 100f; - Text progressText = Text.literal(current + "/" + total); - - ProgressComponent pc = new ProgressComponent(Ico.NETHER_STAR, Text.literal("Timecharms"), progressText, - pcnt, pcntToCol(pcnt)); - - this.addComponent(pc); - } - - private void addEnigmaSoulsComponent(int pos) { - Matcher m = PlayerListMgr.regexAt(pos, ENIGMA_SOULS_PATTERN); - - int current = Integer.parseInt(m.group("current")); - int total = Integer.parseInt(m.group("total")); - float pcnt = ((float) current / (float) total) * 100f; - Text progressText = Text.literal(current + "/" + total); - - ProgressComponent pc = new ProgressComponent(Ico.HEART_OF_THE_SEA, Text.literal("Enigma Souls"), - progressText, pcnt, pcntToCol(pcnt)); - - this.addComponent(pc); - } - - private void addMontezumaComponent(int pos) { - Matcher m = PlayerListMgr.regexAt(pos, MONTEZUMA_PATTERN); - - int current = Integer.parseInt(m.group("current")); - int total = Integer.parseInt(m.group("total")); - float pcnt = ((float) current / (float) total) * 100f; - Text progressText = Text.literal(current + "/" + total); - - ProgressComponent pc = new ProgressComponent(Ico.BONE, Text.literal("Montezuma"), progressText, pcnt, - pcntToCol(pcnt)); - - this.addComponent(pc); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java deleted file mode 100644 index 1ec3771e..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftServerInfoWidget.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -/** - * Special version of the server info widget for the rift! - * - */ -public class RiftServerInfoWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Server Info").formatted(Formatting.LIGHT_PURPLE, Formatting.BOLD); - - public RiftServerInfoWidget() { - super(TITLE, Formatting.LIGHT_PURPLE.getColorValue()); - } - - @Override - public void updateContent() { - this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.LIGHT_PURPLE, 41); - this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42); - } - -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java deleted file mode 100644 index 95a587a9..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/RiftStatsWidget.java +++ /dev/null @@ -1,43 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.Ico; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.TableComponent; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class RiftStatsWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Stats").formatted(Formatting.DARK_AQUA, Formatting.BOLD); - - public RiftStatsWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - Text riftDamage = Widget.simpleEntryText(64, "RDG", Formatting.DARK_PURPLE); - IcoTextComponent rdg = new IcoTextComponent(Ico.DIASWORD, riftDamage); - - Text speed = Widget.simpleEntryText(65, "SPD", Formatting.WHITE); - IcoTextComponent spd = new IcoTextComponent(Ico.SUGAR, speed); - - Text intelligence = Widget.simpleEntryText(66, "INT", Formatting.AQUA); - IcoTextComponent intel = new IcoTextComponent(Ico.ENCHANTED_BOOK, intelligence); - - Text manaRegen = Widget.simpleEntryText(67, "MRG", Formatting.AQUA); - IcoTextComponent mrg = new IcoTextComponent(Ico.DIAMOND, manaRegen); - - TableComponent tc = new TableComponent(2, 2, Formatting.AQUA.getColorValue()); - tc.addToCell(0, 0, rdg); - tc.addToCell(0, 1, spd); - tc.addToCell(1, 0, intel); - tc.addToCell(1, 1, mrg); - - this.addComponent(tc); - } - -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java deleted file mode 100644 index 1f432406..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/tabhud/widget/rift/ShenWidget.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.tabhud.widget.rift; - -import me.xmrvizzy.skyblocker.skyblock.tabhud.util.PlayerListMgr; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.Widget; -import me.xmrvizzy.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -public class ShenWidget extends Widget { - - private static final MutableText TITLE = Text.literal("Shen's Countdown").formatted(Formatting.DARK_AQUA, Formatting.BOLD); - - public ShenWidget() { - super(TITLE, Formatting.DARK_AQUA.getColorValue()); - } - - @Override - public void updateContent() { - this.addComponent(new PlainTextComponent(Text.literal(PlayerListMgr.strAt(70)))); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Boxes.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Boxes.java deleted file mode 100644 index 977d013c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Boxes.java +++ /dev/null @@ -1,50 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import net.minecraft.util.math.Box; -import net.minecraft.util.math.Direction.Axis; -import net.minecraft.util.math.Vec3d; - -public class Boxes { - /** Returns the vector of the min pos of this box. **/ - public static Vec3d getMinVec(Box box) { - return new Vec3d(box.minX, box.minY, box.minZ); - } - - /** Returns the vector of the max pos of this box. **/ - public static Vec3d getMaxVec(Box box) { - return new Vec3d(box.maxX, box.maxY, box.maxZ); - } - - /** Returns the vector of the side lengths of this box. **/ - public static Vec3d getLengthVec(Box box) { - return new Vec3d(box.getLengthX(), box.getLengthY(), box.getLengthZ()); - } - - /** Offsets this box so that minX, minY and minZ are all zero. **/ - public static Box moveToZero(Box box) { - return box.offset(getMinVec(box).negate()); - } - - /** Returns the distance between to oppisite corners of the box. **/ - public static double getCornerLength(Box box) { - return getMinVec(box).distanceTo(getMaxVec(box)); - } - - /** Returns the length of an axis in the box. **/ - public static double getAxisLength(Box box, Axis axis) { - return box.getMax(axis) - box.getMin(axis); - } - - /** Returns a box with each axis multiplied by the amount specified. **/ - public static Box multiply(Box box, double amount) { - return multiply(box, amount, amount, amount); - } - - /** Returns a box with each axis multiplied by the amount specified. **/ - public static Box multiply(Box box, double x, double y, double z) { - return box.expand( - getAxisLength(box, Axis.X) * (x - 1) / 2d, - getAxisLength(box, Axis.Y) * (y - 1) / 2d, - getAxisLength(box, Axis.Z) * (z - 1) / 2d); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java deleted file mode 100644 index aef55687..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Constants.java +++ /dev/null @@ -1,8 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -/** - * Holds generic static constants - */ -public interface Constants { - String LEVEL_EMBLEMS = "\u2E15\u273F\u2741\u2E19\u03B1\u270E\u2615\u2616\u2663\u213B\u2694\u27B6\u26A1\u2604\u269A\u2693\u2620\u269B\u2666\u2660\u2764\u2727\u238A\u1360\u262C\u269D\u29C9\uA214\u32D6\u2E0E\u26A0\uA541\u3020\u30C4\u2948\u2622\u2623\u273E\u269C\u0BD0\u0A6D\u2742\u16C3\u3023\u10F6\u0444\u266A\u266B\u04C3\u26C1\u26C3\u16DD\uA03E\u1C6A\u03A3\u09EB\u2603\u2654\u26C2\u12DE"; -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java deleted file mode 100644 index 3461189c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Http.java +++ /dev/null @@ -1,89 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.net.URI; -import java.net.http.HttpClient; -import java.net.http.HttpClient.Version; -import java.net.http.HttpHeaders; -import java.net.http.HttpRequest; -import java.net.http.HttpRequest.BodyPublishers; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; -import java.time.Duration; -import java.util.zip.GZIPInputStream; -import java.util.zip.InflaterInputStream; - -import me.xmrvizzy.skyblocker.SkyblockerMod; -import net.minecraft.SharedConstants; - -/** - * @implNote All http requests are sent using HTTP 2 - */ -public class Http { - private static final String USER_AGENT = "Skyblocker/" + SkyblockerMod.VERSION + " (" + SharedConstants.getGameVersion().getName() + ")"; - private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() - .connectTimeout(Duration.ofSeconds(10)) - .build(); - - public static String sendGetRequest(String url) throws IOException, InterruptedException { - HttpRequest request = HttpRequest.newBuilder() - .GET() - .header("Accept", "application/json") - .header("Accept-Encoding", "gzip, deflate") - .header("User-Agent", USER_AGENT) - .version(Version.HTTP_2) - .uri(URI.create(url)) - .build(); - - HttpResponse<InputStream> response = HTTP_CLIENT.send(request, BodyHandlers.ofInputStream()); - InputStream decodedInputStream = getDecodedInputStream(response); - String body = new String(decodedInputStream.readAllBytes()); - - return body; - } - - public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException { - HttpRequest request = HttpRequest.newBuilder() - .method("HEAD", BodyPublishers.noBody()) - .header("User-Agent", USER_AGENT) - .version(Version.HTTP_2) - .uri(URI.create(url)) - .build(); - - HttpResponse<Void> response = HTTP_CLIENT.send(request, BodyHandlers.discarding()); - return response.headers(); - } - - private static InputStream getDecodedInputStream(HttpResponse<InputStream> response) { - String encoding = getContentEncoding(response); - - try { - switch (encoding) { - case "": - return response.body(); - case "gzip": - return new GZIPInputStream(response.body()); - case "deflate": - return new InflaterInputStream(response.body()); - default: - throw new UnsupportedOperationException("The server sent content in an unexpected encoding: " + encoding); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private static String getContentEncoding(HttpResponse<InputStream> response) { - return response.headers().firstValue("Content-Encoding").orElse(""); - } - - public static String getEtag(HttpHeaders headers) { - return headers.firstValue("Etag").orElse(""); - } - - public static String getLastModified(HttpHeaders headers) { - return headers.firstValue("Last-Modified").orElse(""); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java deleted file mode 100644 index b8192b45..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/ItemUtils.java +++ /dev/null @@ -1,111 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import com.mojang.brigadier.exceptions.CommandSyntaxException; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.item.TooltipContext; -import net.minecraft.item.ItemStack; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.StringNbtReader; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.regex.Pattern; - -public class ItemUtils { - private final static Pattern WHITESPACES = Pattern.compile("^\\s*$"); - - public static List<Text> getTooltip(ItemStack item) { - MinecraftClient client = MinecraftClient.getInstance(); - return client.player == null || item == null ? Collections.emptyList() : item.getTooltip(client.player, TooltipContext.Default.BASIC); - } - - public static List<String> getTooltipStrings(ItemStack item) { - List<Text> lines = getTooltip(item); - List<String> list = new ArrayList<>(); - - for (Text line : lines) { - String string = line.getString(); - if (!WHITESPACES.matcher(string).matches()) - list.add(string); - } - - return list; - } - - @Nullable - public static Durability getDurability(ItemStack stack) { - if (!Utils.isOnSkyblock() || !SkyblockerConfigManager.get().locations.dwarvenMines.enableDrillFuel || stack.isEmpty()) { - return null; - } - - NbtCompound tag = stack.getNbt(); - if (tag == null || !tag.contains("ExtraAttributes")) { - return null; - } - - NbtCompound extraAttributes = tag.getCompound("ExtraAttributes"); - if (!extraAttributes.contains("drill_fuel") && !extraAttributes.getString("id").equals("PICKONIMBUS")) { - return null; - } - - int current = 0; - int max = 0; - String clearFormatting; - - for (String line : ItemUtils.getTooltipStrings(stack)) { - clearFormatting = Formatting.strip(line); - if (line.contains("Fuel: ")) { - if (clearFormatting != null) { - String clear = Pattern.compile("[^0-9 /]").matcher(clearFormatting).replaceAll("").trim(); - String[] split = clear.split("/"); - current = Integer.parseInt(split[0]); - max = Integer.parseInt(split[1]) * 1000; - return new Durability(current, max); - } - } else if (line.contains("uses.")) { - if (clearFormatting != null) { - int startIndex = clearFormatting.lastIndexOf("after") + 6; - int endIndex = clearFormatting.indexOf("uses", startIndex); - if (startIndex >= 0 && endIndex > startIndex) { - String usesString = clearFormatting.substring(startIndex, endIndex).trim(); - current = Integer.parseInt(usesString); - max = 5000; - } - return new Durability(current, max); - } - } - } - - return null; - } - - public static ItemStack getSkyblockerStack() { - try { - return ItemStack.fromNbt(StringNbtReader.parse("{id:\"minecraft:player_head\",Count:1,tag:{SkullOwner:{Id:[I;-300151517,-631415889,-1193921967,-1821784279],Properties:{textures:[{Value:\"e3RleHR1cmVzOntTS0lOOnt1cmw6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDdjYzY2ODc0MjNkMDU3MGQ1NTZhYzUzZTA2NzZjYjU2M2JiZGQ5NzE3Y2Q4MjY5YmRlYmVkNmY2ZDRlN2JmOCJ9fX0=\"}]}}}}")); - } catch (CommandSyntaxException e) { - throw new RuntimeException(e); - } - } - - public static String getItemId(ItemStack itemStack) { - if (itemStack == null) return null; - - NbtCompound nbt = itemStack.getNbt(); - if (nbt != null && nbt.contains("ExtraAttributes")) { - NbtCompound extraAttributes = nbt.getCompound("ExtraAttributes"); - if (extraAttributes.contains("id")) { - return extraAttributes.getString("id"); - } - } - - return null; - } - - public record Durability(int current, int max) { - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java b/src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java deleted file mode 100644 index 29b39aa3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/NEURepo.java +++ /dev/null @@ -1,101 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import me.xmrvizzy.skyblocker.SkyblockerMod; -import me.xmrvizzy.skyblocker.skyblock.itemlist.ItemRegistry; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; -import org.eclipse.jgit.api.Git; -import org.eclipse.jgit.api.errors.TransportException; -import org.eclipse.jgit.errors.RepositoryNotFoundException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -/** - * Initializes the NEU repo, which contains item metadata and fairy souls location data. Clones the repo if it does not exist and checks for updates. Use {@link #runAsyncAfterLoad(Runnable)} to run code after the repo is initialized. - */ -public class NEURepo { - private static final Logger LOGGER = LoggerFactory.getLogger(NEURepo.class); - public static final String REMOTE_REPO_URL = "https://github.com/NotEnoughUpdates/NotEnoughUpdates-REPO.git"; - public static final Path LOCAL_REPO_DIR = SkyblockerMod.CONFIG_DIR.resolve("item-repo"); - private static final CompletableFuture<Void> REPO_INITIALIZED = initRepository(); - - /** - * Adds command to update repository manually from ingame. - * <p></p> - * TODO A button could be added to the settings menu that will trigger this command. - */ - public static void init() { - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> - dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE) - .then(ClientCommandManager.literal("updaterepository").executes(context -> { - deleteAndDownloadRepository(); - return 1; - })))); - } - - private static CompletableFuture<Void> initRepository() { - return CompletableFuture.runAsync(() -> { - try { - if (Files.isDirectory(NEURepo.LOCAL_REPO_DIR)) { - try (Git localRepo = Git.open(NEURepo.LOCAL_REPO_DIR.toFile())) { - localRepo.pull().setRebase(true).call(); - LOGGER.info("[Skyblocker] NEU Repository Updated"); - } - } else { - Git.cloneRepository().setURI(REMOTE_REPO_URL).setDirectory(NEURepo.LOCAL_REPO_DIR.toFile()).setBranchesToClone(List.of("refs/heads/master")).setBranch("refs/heads/master").call().close(); - LOGGER.info("[Skyblocker] NEU Repository Downloaded"); - } - } catch (TransportException e){ - LOGGER.error("[Skyblocker] Transport operation failed. Most likely unable to connect to the remote NEU repo on github", e); - } catch (RepositoryNotFoundException e) { - LOGGER.warn("[Skyblocker] Local NEU Repository not found or corrupted, downloading new one", e); - deleteAndDownloadRepository(); - } catch (Exception e) { - LOGGER.error("[Skyblocker] Encountered unknown exception while initializing NEU Repository", e); - } - }); - } - - private static void deleteAndDownloadRepository() { - CompletableFuture.runAsync(() -> { - try { - ItemRegistry.filesImported = false; - File dir = NEURepo.LOCAL_REPO_DIR.toFile(); - recursiveDelete(dir); - } catch (Exception ex) { - if (MinecraftClient.getInstance().player != null) - MinecraftClient.getInstance().player.sendMessage(Text.translatable("skyblocker.updaterepository.failed"), false); - return; - } - initRepository(); - }); - } - - @SuppressWarnings("ResultOfMethodCallIgnored") - private static void recursiveDelete(File dir) { - File[] children; - if (dir.isDirectory() && !Files.isSymbolicLink(dir.toPath()) && (children = dir.listFiles()) != null) { - for (File child : children) { - recursiveDelete(child); - } - } - dir.delete(); - } - - /** - * Runs the given runnable after the NEU repo is initialized. - * @param runnable the runnable to run - * @return a completable future of the given runnable - */ - public static CompletableFuture<Void> runAsyncAfterLoad(Runnable runnable) { - return REPO_INITIALIZED.thenRunAsync(runnable); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java deleted file mode 100644 index 4f32292c..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/PosUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import net.minecraft.util.math.BlockPos; - -public final class PosUtils { - public static BlockPos parsePosString(String posData) { - String[] posArray = posData.split(","); - return new BlockPos(Integer.parseInt(posArray[0]), Integer.parseInt(posArray[1]), Integer.parseInt(posArray[2])); - } - - public static String getPosString(BlockPos blockPos) { - return blockPos.getX() + "," + blockPos.getY() + "," + blockPos.getZ(); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java deleted file mode 100644 index cb75f20f..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/SlayerUtils.java +++ /dev/null @@ -1,54 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.Entity; -import net.minecraft.entity.decoration.ArmorStandEntity; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.List; - -//TODO Slayer Packet system that can provide information about the current slayer boss, abstract so that different bosses can have different info -public class SlayerUtils { - private static final Logger LOGGER = LoggerFactory.getLogger(SlayerUtils.class); - - //TODO: Cache this, probably included in Packet system - public static List<Entity> getEntityArmorStands(Entity entity) { - return entity.getEntityWorld().getOtherEntities(entity, entity.getBoundingBox().expand(1F, 2.5F, 1F), x -> x instanceof ArmorStandEntity && x.hasCustomName()); - } - - //Eventually this should be modified so that if you hit a slayer boss all slayer features will work on that boss. - public static Entity getSlayerEntity() { - if (MinecraftClient.getInstance().world != null) { - for (Entity entity : MinecraftClient.getInstance().world.getEntities()) { - //Check if entity is Bloodfiend - if (entity.hasCustomName() && entity.getCustomName().getString().contains("Bloodfiend")) { - //Grab the players username - String username = MinecraftClient.getInstance().getSession().getUsername(); - //Check all armor stands around the boss - for (Entity armorStand : getEntityArmorStands(entity)) { - //Check if the display name contains the players username - if (armorStand.getDisplayName().getString().contains(username)) { - return entity; - } - } - } - } - } - return null; - } - - public static boolean isInSlayer() { - try { - for (int i = 0; i < Utils.STRING_SCOREBOARD.size(); i++) { - String line = Utils.STRING_SCOREBOARD.get(i); - - if (line.contains("Slay the boss!")) return true; - } - } catch (NullPointerException e) { - LOGGER.error("[Skyblocker] Error while checking if player is in slayer", e); - } - - return false; - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java deleted file mode 100644 index cee2f2ad..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/Utils.java +++ /dev/null @@ -1,370 +0,0 @@ -package me.xmrvizzy.skyblocker.utils; - -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; - -import it.unimi.dsi.fastutil.objects.ObjectArrayList; -import me.xmrvizzy.skyblocker.events.SkyblockEvents; -import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip; -import me.xmrvizzy.skyblocker.skyblock.rift.TheRift; -import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler; -import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.networking.v1.PacketSender; -import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayNetworkHandler; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.client.network.PlayerListEntry; -import net.minecraft.scoreboard.Scoreboard; -import net.minecraft.scoreboard.ScoreboardDisplaySlot; -import net.minecraft.scoreboard.ScoreboardObjective; -import net.minecraft.scoreboard.ScoreboardPlayerScore; -import net.minecraft.scoreboard.Team; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Collections; -import java.util.List; - -/** - * Utility variables and methods for retrieving Skyblock related information. - */ -public class Utils { - private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class); - private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", ""); - private static final String PROFILE_PREFIX = "Profile: "; - private static boolean isOnHypixel = false; - private static boolean isOnSkyblock = false; - private static boolean isInDungeons = false; - private static boolean isInjected = false; - /** - * The following fields store data returned from /locraw: {@link #profile}, {@link #server}, {@link #gameType}, {@link #locationRaw}, and {@link #map}. - */ - @SuppressWarnings("JavadocDeclaration") - private static String profile = ""; - private static String server = ""; - private static String gameType = ""; - private static String locationRaw = ""; - private static String map = ""; - private static long clientWorldJoinTime = 0; - private static boolean sentLocRaw = false; - private static boolean canSendLocRaw = false; - - /** - * @implNote The parent text will always be empty, the actual text content is inside the text's siblings. - */ - public static final ObjectArrayList<Text> TEXT_SCOREBOARD = new ObjectArrayList<>(); - public static final ObjectArrayList<String> STRING_SCOREBOARD = new ObjectArrayList<>(); - - public static boolean isOnHypixel() { - return isOnHypixel; - } - - public static boolean isOnSkyblock() { - return isOnSkyblock; - } - - public static boolean isInDungeons() { - return isInDungeons; - } - - public static boolean isInTheRift() { - return getLocationRaw().equals(TheRift.LOCATION); - } - - public static boolean isInjected() { - return isInjected; - } - - /** - * @return the profile parsed from the player list. - */ - public static String getProfile() { - return profile; - } - - /** - * @return the server parsed from /locraw. - */ - public static String getServer() { - return server; - } - - /** - * @return the game type parsed from /locraw. - */ - public static String getGameType() { - return gameType; - } - - /** - * @return the location raw parsed from /locraw. - */ - public static String getLocationRaw() { - return locationRaw; - } - - /** - * @return the map parsed from /locraw. - */ - public static String getMap() { - return map; - } - - public static void init() { - ClientPlayConnectionEvents.JOIN.register(Utils::onClientWorldJoin); - ClientReceiveMessageEvents.ALLOW_GAME.register(Utils::onChatMessage); - ClientReceiveMessageEvents.GAME_CANCELED.register(Utils::onChatMessage); // Somehow this works even though onChatMessage returns a boolean - } - - /** - * Updates all the fields stored in this class from the sidebar, player list, and /locraw. - */ - public static void update() { - MinecraftClient client = MinecraftClient.getInstance(); - updateScoreboard(client); - updatePlayerPresenceFromScoreboard(client); - updateFromPlayerList(client); - updateLocRaw(); - } - - /** - * Updates {@link #isOnSkyblock}, {@link #isInDungeons}, and {@link #isInjected} from the scoreboard. - */ - public static void updatePlayerPresenceFromScoreboard(MinecraftClient client) { - List<String> sidebar = STRING_SCOREBOARD; - - FabricLoader fabricLoader = FabricLoader.getInstance(); - if ((client.world == null || client.isInSingleplayer() || sidebar == null || sidebar.isEmpty())) { - if (fabricLoader.isDevelopmentEnvironment()) { - sidebar = Collections.emptyList(); - } else { - isOnSkyblock = false; - isInDungeons = false; - return; - } - } - - if (sidebar.isEmpty() && !fabricLoader.isDevelopmentEnvironment()) return; - String string = sidebar.toString(); - - if (fabricLoader.isDevelopmentEnvironment() || isConnectedToHypixel(client)) { - if (!isOnHypixel) { - isOnHypixel = true; - } - if (fabricLoader.isDevelopmentEnvironment() || sidebar.get(0).contains("SKYBLOCK") || sidebar.get(0).contains("SKIBLOCK")) { - if (!isOnSkyblock) { - if (!isInjected) { - isInjected = true; - ItemTooltipCallback.EVENT.register(PriceInfoTooltip::onInjectTooltip); - } - isOnSkyblock = true; - SkyblockEvents.JOIN.invoker().onSkyblockJoin(); - } - } else { - onLeaveSkyblock(); - } - isInDungeons = fabricLoader.isDevelopmentEnvironment() || isOnSkyblock && string.contains("The Catacombs"); - } else if (isOnHypixel) { - isOnHypixel = false; - onLeaveSkyblock(); - } - } - - private static boolean isConnectedToHypixel(MinecraftClient client) { - String serverAddress = (client.getCurrentServerEntry() != null) ? client.getCurrentServerEntry().address.toLowerCase() : ""; - String serverBrand = (client.player != null && client.player.networkHandler != null && client.player.networkHandler.getBrand() != null) ? client.player.networkHandler.getBrand() : ""; - - return serverAddress.equalsIgnoreCase(ALTERNATE_HYPIXEL_ADDRESS) || serverAddress.contains("hypixel.net") || serverAddress.contains("hypixel.io") || serverBrand.contains("Hypixel BungeeCord"); - } - - private static void onLeaveSkyblock() { - if (isOnSkyblock) { - isOnSkyblock = false; - isInDungeons = false; - SkyblockEvents.LEAVE.invoker().onSkyblockLeave(); - } - } - - public static String getLocation() { - String location = null; - List<String> sidebarLines = STRING_SCOREBOARD; - try { - if (sidebarLines != null) { - for (String sidebarLine : sidebarLines) { - if (sidebarLine.contains("⏣")) location = sidebarLine; - if (sidebarLine.contains("ф")) location = sidebarLine; //Rift - } - if (location == null) location = "Unknown"; - location = location.strip(); - } - } catch (IndexOutOfBoundsException e) { - LOGGER.error("[Skyblocker] Failed to get location from sidebar", e); - } - return location; - } - - public static double getPurse() { - String purseString = null; - double purse = 0; - - List<String> sidebarLines = STRING_SCOREBOARD; - try { - - if (sidebarLines != null) { - for (String sidebarLine : sidebarLines) { - if (sidebarLine.contains("Piggy:")) purseString = sidebarLine; - if (sidebarLine.contains("Purse:")) purseString = sidebarLine; - } - } - if (purseString != null) purse = Double.parseDouble(purseString.replaceAll("[^0-9.]", "").strip()); - else purse = 0; - - } catch (IndexOutOfBoundsException e) { - LOGGER.error("[Skyblocker] Failed to get purse from sidebar", e); - } - return purse; - } - - public static int getBits() { - int bits = 0; - String bitsString = null; - List<String> sidebarLines = STRING_SCOREBOARD; - try { - if (sidebarLines != null) { - for (String sidebarLine : sidebarLines) { - if (sidebarLine.contains("Bits")) bitsString = sidebarLine; - } - } - if (bitsString != null) { - bits = Integer.parseInt(bitsString.replaceAll("[^0-9.]", "").strip()); - } - } catch (IndexOutOfBoundsException e) { - LOGGER.error("[Skyblocker] Failed to get bits from sidebar", e); - } - return bits; - } - - private static void updateScoreboard(MinecraftClient client) { - try { - TEXT_SCOREBOARD.clear(); - STRING_SCOREBOARD.clear(); - - ClientPlayerEntity player = client.player; - if (player == null) return; - - Scoreboard scoreboard = player.getScoreboard(); - ScoreboardObjective objective = scoreboard.getObjectiveForSlot(ScoreboardDisplaySlot.FROM_ID.apply(1)); - ObjectArrayList<Text> textLines = new ObjectArrayList<>(); - ObjectArrayList<String> stringLines = new ObjectArrayList<>(); - - for (ScoreboardPlayerScore score : scoreboard.getAllPlayerScores(objective)) { - Team team = scoreboard.getPlayerTeam(score.getPlayerName()); - - if (team != null) { - Text textLine = Text.empty().append(team.getPrefix().copy()).append(team.getSuffix().copy()); - String strLine = team.getPrefix().getString() + team.getSuffix().getString(); - - if (!strLine.trim().isEmpty()) { - String formatted = Formatting.strip(strLine); - - textLines.add(textLine); - stringLines.add(formatted); - } - } - } - - if (objective != null) { - stringLines.add(objective.getDisplayName().getString()); - textLines.add(Text.empty().append(objective.getDisplayName().copy())); - - Collections.reverse(stringLines); - Collections.reverse(textLines); - } - - TEXT_SCOREBOARD.addAll(textLines); - STRING_SCOREBOARD.addAll(stringLines); - } catch (NullPointerException e) { - //Do nothing - } - } - - private static void updateFromPlayerList(MinecraftClient client) { - if (client.getNetworkHandler() == null) { - return; - } - for (PlayerListEntry playerListEntry : client.getNetworkHandler().getPlayerList()) { - if (playerListEntry.getDisplayName() == null) { - continue; - } - String name = playerListEntry.getDisplayName().getString(); - if (name.startsWith(PROFILE_PREFIX)) { - profile = name.substring(PROFILE_PREFIX.length()); - } - } - } - - public static void onClientWorldJoin(ClientPlayNetworkHandler handler, PacketSender sender, MinecraftClient client) { - clientWorldJoinTime = System.currentTimeMillis(); - resetLocRawInfo(); - } - - /** - * Sends /locraw to the server if the player is on skyblock and on a new island. - */ - private static void updateLocRaw() { - if (isOnSkyblock) { - long currentTime = System.currentTimeMillis(); - if (!sentLocRaw && canSendLocRaw && currentTime > clientWorldJoinTime + 1000) { - MessageScheduler.INSTANCE.sendMessageAfterCooldown("/locraw"); - sentLocRaw = true; - canSendLocRaw = false; - } - } else { - resetLocRawInfo(); - } - } - - /** - * Parses the /locraw reply from the server - * - * @return not display the message in chat is the command is sent by the mod - */ - public static boolean onChatMessage(Text text, boolean overlay) { - String message = text.getString(); - if (message.startsWith("{\"server\":") && message.endsWith("}")) { - JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject(); - if (locRaw.has("server")) { - server = locRaw.get("server").getAsString(); - if (locRaw.has("gameType")) { - gameType = locRaw.get("gameType").getAsString(); - } - if (locRaw.has("mode")) { - locationRaw = locRaw.get("mode").getAsString(); - } - if (locRaw.has("map")) { - map = locRaw.get("map").getAsString(); - } - - boolean shouldFilter = !sentLocRaw; - sentLocRaw = false; - - return shouldFilter; - } - } - return true; - } - - private static void resetLocRawInfo() { - sentLocRaw = false; - canSendLocRaw = true; - server = ""; - gameType = ""; - locationRaw = ""; - map = ""; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatFilterResult.java b/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatFilterResult.java deleted file mode 100644 index cd364eb5..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatFilterResult.java +++ /dev/null @@ -1,18 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.chat; - -import net.minecraft.client.resource.language.I18n; -public enum ChatFilterResult { - // Skip this one / no action - PASS, - // Filter - FILTER, - // Move to action bar - ACTION_BAR; - // Skip remaining checks, don't filter - // null - - @Override - public String toString() { - return I18n.translate("text.autoconfig.skyblocker.option.messages.chatFilterResult." + name()); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatMessageListener.java b/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatMessageListener.java deleted file mode 100644 index 7bb75947..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatMessageListener.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.chat; - -import me.xmrvizzy.skyblocker.skyblock.barn.HungryHiker; -import me.xmrvizzy.skyblocker.skyblock.barn.TreasureHunter; -import me.xmrvizzy.skyblocker.skyblock.dungeon.Reparty; -import me.xmrvizzy.skyblocker.skyblock.dungeon.ThreeWeirdos; -import me.xmrvizzy.skyblocker.skyblock.dungeon.Trivia; -import me.xmrvizzy.skyblocker.skyblock.dwarven.Fetchur; -import me.xmrvizzy.skyblocker.skyblock.dwarven.Puzzler; -import me.xmrvizzy.skyblocker.skyblock.filters.*; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; -import net.fabricmc.fabric.api.event.Event; -import net.fabricmc.fabric.api.event.EventFactory; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.network.ClientPlayerEntity; -import net.minecraft.text.Text; - -@FunctionalInterface -public interface ChatMessageListener { - /** - * An event called when a game message is received. Register your listeners in {@link ChatMessageListener#init()}. - */ - Event<ChatMessageListener> EVENT = EventFactory.createArrayBacked(ChatMessageListener.class, - (listeners) -> (message, asString) -> { - for (ChatMessageListener listener : listeners) { - ChatFilterResult result = listener.onMessage(message, asString); - if (result != ChatFilterResult.PASS) return result; - } - return ChatFilterResult.PASS; - }); - - /** - * Registers {@link ChatMessageListener}s to {@link ChatMessageListener#EVENT} and registers {@link ChatMessageListener#EVENT} to {@link ClientReceiveMessageEvents#ALLOW_GAME} - */ - static void init() { - ChatMessageListener[] listeners = new ChatMessageListener[]{ - // Features - new Fetchur(), - new Puzzler(), - new Reparty(), - new ThreeWeirdos(), - new Trivia(), - new TreasureHunter(), - new HungryHiker(), - // Filters - new AbilityFilter(), - new AdFilter(), - new AoteFilter(), - new ComboFilter(), - new HealFilter(), - new ImplosionFilter(), - new MoltenWaveFilter(), - new TeleportPadFilter(), - new AutopetFilter(), - new ShowOffFilter() - }; - // Register all listeners to EVENT - for (ChatMessageListener listener : listeners) { - EVENT.register(listener); - } - // Register EVENT to ClientReceiveMessageEvents.ALLOW_GAME from fabric api - ClientReceiveMessageEvents.ALLOW_GAME.register((message, overlay) -> { - if (!Utils.isOnSkyblock()) { - return true; - } - ChatFilterResult result = EVENT.invoker().onMessage(message, message.getString()); - switch (result) { - case ACTION_BAR -> { - if (overlay) { - return true; - } - ClientPlayerEntity player = MinecraftClient.getInstance().player; - if (player != null) { - player.sendMessage(message, true); - return false; - } - } - case FILTER -> { - return false; - } - } - return true; - }); - } - - ChatFilterResult onMessage(Text message, String asString); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListener.java b/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListener.java deleted file mode 100644 index 3b363a85..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListener.java +++ /dev/null @@ -1,30 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.chat; - -import net.minecraft.text.Text; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public abstract class ChatPatternListener implements ChatMessageListener { - protected static final String NUMBER = "-?[0-9]{1,3}(?>,[0-9]{3})*(?:\\.[1-9])?"; - public final Pattern pattern; - - public ChatPatternListener(String pattern) { - this.pattern = Pattern.compile(pattern); - } - - @Override - public final ChatFilterResult onMessage(Text message, String asString) { - ChatFilterResult state = state(); - if (state == ChatFilterResult.PASS) return ChatFilterResult.PASS; - Matcher m = pattern.matcher(asString); - if (m.matches() && onMatch(message, m)) { - return state; - } - return ChatFilterResult.PASS; - } - - protected abstract ChatFilterResult state(); - - protected abstract boolean onMatch(Text message, Matcher matcher); -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java b/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java deleted file mode 100644 index 6196fec0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/discord/DiscordRPCManager.java +++ /dev/null @@ -1,121 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.discord; - - -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.events.SkyblockEvents; -import me.xmrvizzy.skyblocker.utils.Utils; -import meteordevelopment.discordipc.DiscordIPC; -import meteordevelopment.discordipc.RichPresence; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.text.DecimalFormat; -import java.util.concurrent.CompletableFuture; - -/** - * Manages the discord rich presence. Automatically connects to discord and displays a customizable activity when playing Skyblock. - */ -public class DiscordRPCManager { - public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###,###.##"); - public static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Discord RPC"); - /** - * The update task used to avoid multiple update tasks running simultaneously. - */ - public static CompletableFuture<Void> updateTask; - public static long startTimeStamp; - public static int cycleCount; - - public static void init() { - SkyblockEvents.LEAVE.register(DiscordRPCManager::initAndUpdatePresence); - SkyblockEvents.JOIN.register(() -> { - startTimeStamp = System.currentTimeMillis(); - initAndUpdatePresence(true); - }); - } - - /** - * Checks the {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. - */ - public static void updateDataAndPresence() { - // If the custom message is empty, discord will keep the last message, this is can serve as a default if the user doesn't want a custom message - if (SkyblockerConfigManager.get().richPresence.customMessage.isEmpty()) { - SkyblockerConfigManager.get().richPresence.customMessage = "Playing Skyblock"; - SkyblockerConfigManager.save(); - } - if (SkyblockerConfigManager.get().richPresence.cycleMode) cycleCount = (cycleCount + 1) % 3; - initAndUpdatePresence(); - } - - /** - * @see #initAndUpdatePresence(boolean) - */ - private static void initAndUpdatePresence() { - initAndUpdatePresence(false); - } - - /** - * Updates discord presence asynchronously. - * <p> - * When the {@link #updateTask previous update} does not exist or {@link CompletableFuture#isDone() has completed}: - * <p> - * Connects to discord if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}, - * the player {@link Utils#isOnSkyblock() is on Skyblock}, and {@link DiscordIPC#isConnected() discord is not already connected}. - * Updates the presence if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled} - * and the player {@link Utils#isOnSkyblock() is on Skyblock}. - * Stops the connection if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled} - * or the player {@link Utils#isOnSkyblock() is not on Skyblock} and {@link DiscordIPC#isConnected() discord is connected}. - * Saves the update task in {@link #updateTask} - * - * @param initialization whether this is the first time the presence is being updates. If {@code true}, a message will be logged - * if {@link me.xmrvizzy.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}. - */ - private static void initAndUpdatePresence(boolean initialization) { - if (updateTask == null || updateTask.isDone()) { - updateTask = CompletableFuture.runAsync(() -> { - if (SkyblockerConfigManager.get().richPresence.enableRichPresence && Utils.isOnSkyblock()) { - if (!DiscordIPC.isConnected()) { - if (DiscordIPC.start(934607927837356052L, null)) { - LOGGER.info("Discord RPC started successfully"); - } else { - LOGGER.error("Discord RPC failed to start"); - return; - } - } - DiscordIPC.setActivity(buildPresence()); - } else if (DiscordIPC.isConnected()) { - DiscordIPC.stop(); - LOGGER.info("Discord RPC stopped"); - } else if (initialization) { - LOGGER.info("Discord RPC is currently disabled"); - } - }); - } - } - - public static RichPresence buildPresence() { - RichPresence presence = new RichPresence(); - presence.setLargeImage("skyblocker-default", null); - presence.setStart(startTimeStamp); - presence.setDetails(SkyblockerConfigManager.get().richPresence.customMessage); - presence.setState(getInfo()); - return presence; - } - - public static String getInfo() { - String info = null; - if (!SkyblockerConfigManager.get().richPresence.cycleMode) { - switch (SkyblockerConfigManager.get().richPresence.info) { - case BITS -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits()); - case PURSE -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse()); - case LOCATION -> info = Utils.getLocation(); - } - } else if (SkyblockerConfigManager.get().richPresence.cycleMode) { - switch (cycleCount) { - case 0 -> info = "Bits: " + DECIMAL_FORMAT.format(Utils.getBits()); - case 1 -> info = "Purse: " + DECIMAL_FORMAT.format(Utils.getPurse()); - case 2 -> info = Utils.getLocation(); - } - } - return info; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/FrustumUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/FrustumUtils.java deleted file mode 100644 index d4dd8b18..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/FrustumUtils.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render; - -import me.xmrvizzy.skyblocker.mixin.accessor.WorldRendererAccessor; -import me.xmrvizzy.skyblocker.mixin.accessor.FrustumInvoker; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.Frustum; -import net.minecraft.util.math.Box; - -public class FrustumUtils { - public static Frustum getFrustum() { - return ((WorldRendererAccessor) MinecraftClient.getInstance().worldRenderer).getFrustum(); - } - - public static boolean isVisible(Box box) { - return getFrustum().isVisible(box); - } - - public static boolean isVisible(double minX, double minY, double minZ, double maxX, double maxY, double maxZ) { - return ((FrustumInvoker) getFrustum()).invokeIsVisible(minX, minY, minZ, maxX, maxY, maxZ); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java deleted file mode 100644 index 907896e2..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/RenderHelper.java +++ /dev/null @@ -1,247 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render; - -import com.mojang.blaze3d.platform.GlStateManager.DstFactor; -import com.mojang.blaze3d.platform.GlStateManager.SrcFactor; -import com.mojang.blaze3d.systems.RenderSystem; -import me.x150.renderer.render.Renderer3d; -import me.xmrvizzy.skyblocker.mixin.accessor.BeaconBlockEntityRendererInvoker; -import me.xmrvizzy.skyblocker.utils.render.culling.OcclusionCulling; -import me.xmrvizzy.skyblocker.utils.render.title.Title; -import me.xmrvizzy.skyblocker.utils.render.title.TitleContainer; -import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.render.*; -import net.minecraft.client.render.VertexFormat.DrawMode; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.sound.SoundEvents; -import net.minecraft.text.OrderedText; -import net.minecraft.text.Text; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Box; -import net.minecraft.util.math.Vec3d; -import org.joml.Matrix3f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.lwjgl.opengl.GL11; - -import java.awt.*; - -public class RenderHelper { - private static final Vec3d ONE = new Vec3d(1, 1, 1); - private static final int MAX_OVERWORLD_BUILD_HEIGHT = 319; - private static final MinecraftClient client = MinecraftClient.getInstance(); - - public static void renderFilledThroughWallsWithBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { - renderFilledThroughWalls(context, pos, colorComponents, alpha); - renderBeaconBeam(context, pos, colorComponents); - } - - public static void renderFilledThroughWalls(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { - if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) { - Renderer3d.renderThroughWalls(); - renderFilled(context, pos, colorComponents, alpha); - Renderer3d.stopRenderThroughWalls(); - } - } - - public static void renderFilledIfVisible(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { - if (OcclusionCulling.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) { - renderFilled(context, pos, colorComponents, alpha); - } - } - - private static void renderFilled(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) { - Renderer3d.renderFilled(context.matrixStack(), new Color(colorComponents[0], colorComponents[1], colorComponents[2], alpha), Vec3d.of(pos), ONE); - } - - private static void renderBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents) { - if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, MAX_OVERWORLD_BUILD_HEIGHT, pos.getZ() + 1)) { - MatrixStack matrices = context.matrixStack(); - Vec3d camera = context.camera().getPos(); - - matrices.push(); - matrices.translate(pos.getX() - camera.getX(), pos.getY() - camera.getY(), pos.getZ() - camera.getZ()); - - Tessellator tessellator = RenderSystem.renderThreadTesselator(); - BufferBuilder buffer = tessellator.getBuffer(); - VertexConsumerProvider.Immediate consumer = VertexConsumerProvider.immediate(buffer); - - BeaconBlockEntityRendererInvoker.renderBeam(matrices, consumer, context.tickDelta(), context.world().getTime(), 0, MAX_OVERWORLD_BUILD_HEIGHT, colorComponents); - - consumer.draw(); - matrices.pop(); - } - } - - /** - * Renders the outline of a box with the specified color components and line width. - * This does not use renderer since renderer draws outline using debug lines with a fixed width. - */ - public static void renderOutline(WorldRenderContext context, Box box, float[] colorComponents, float lineWidth) { - if (FrustumUtils.isVisible(box)) { - MatrixStack matrices = context.matrixStack(); - Vec3d camera = context.camera().getPos(); - Tessellator tessellator = RenderSystem.renderThreadTesselator(); - BufferBuilder buffer = tessellator.getBuffer(); - - RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram); - RenderSystem.setShaderColor(1f, 1f, 1f, 1f); - RenderSystem.lineWidth(lineWidth); - RenderSystem.disableCull(); - RenderSystem.enableDepthTest(); - - matrices.push(); - matrices.translate(-camera.getX(), -camera.getY(), -camera.getZ()); - - buffer.begin(DrawMode.LINES, VertexFormats.LINES); - WorldRenderer.drawBox(matrices, buffer, box, colorComponents[0], colorComponents[1], colorComponents[2], 1f); - tessellator.draw(); - - matrices.pop(); - RenderSystem.lineWidth(1f); - RenderSystem.enableCull(); - RenderSystem.disableDepthTest(); - } - } - - /** - * Draws lines from point to point.<br><br> - * <p> - * Tip: To draw lines from the center of a block, offset the X, Y and Z each by 0.5 - * - * @param context The WorldRenderContext which supplies the matrices and tick delta - * @param points The points from which to draw lines between - * @param colorComponents An array of R, G and B color components - * @param alpha The alpha of the lines - * @param lineWidth The width of the lines - */ - public static void renderLinesFromPoints(WorldRenderContext context, Vec3d[] points, float[] colorComponents, float alpha, float lineWidth) { - Vec3d camera = context.camera().getPos(); - MatrixStack matrices = context.matrixStack(); - - matrices.push(); - matrices.translate(-camera.x, -camera.y, -camera.z); - - Tessellator tessellator = RenderSystem.renderThreadTesselator(); - BufferBuilder buffer = tessellator.getBuffer(); - Matrix4f projectionMatrix = matrices.peek().getPositionMatrix(); - Matrix3f normalMatrix = matrices.peek().getNormalMatrix(); - - GL11.glEnable(GL11.GL_LINE_SMOOTH); - GL11.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST); - - RenderSystem.setShader(GameRenderer::getRenderTypeLinesProgram); - RenderSystem.setShaderColor(1f, 1f, 1f, 1f); - RenderSystem.lineWidth(lineWidth); - RenderSystem.enableBlend(); - RenderSystem.blendFunc(SrcFactor.SRC_ALPHA, DstFactor.ONE_MINUS_SRC_ALPHA); - RenderSystem.disableCull(); - RenderSystem.enableDepthTest(); - - buffer.begin(DrawMode.LINE_STRIP, VertexFormats.LINES); - - for (int i = 0; i < points.length; i++) { - Vec3d point = points[i]; - Vec3d nextPoint = (i + 1 == points.length) ? points[i - 1] : points[i + 1]; - Vector3f normalVec = new Vector3f((float) nextPoint.getX(), (float) nextPoint.getY(), (float) nextPoint.getZ()).sub((float) point.getX(), (float) point.getY(), (float) point.getZ()).normalize(); - - buffer - .vertex(projectionMatrix, (float) point.getX(), (float) point.getY(), (float) point.getZ()) - .color(colorComponents[0], colorComponents[1], colorComponents[2], alpha) - .normal(normalMatrix, normalVec.x, normalVec.y, normalVec.z) - .next(); - } - - tessellator.draw(); - - matrices.pop(); - GL11.glDisable(GL11.GL_LINE_SMOOTH); - RenderSystem.lineWidth(1f); - RenderSystem.disableBlend(); - RenderSystem.defaultBlendFunc(); - RenderSystem.enableCull(); - RenderSystem.disableDepthTest(); - } - - public static void renderText(WorldRenderContext context, Text text, Vec3d pos, boolean seeThrough) { - renderText(context, text, pos, 1, seeThrough); - } - - public static void renderText(WorldRenderContext context, Text text, Vec3d pos, float scale, boolean seeThrough) { - renderText(context, text, pos, scale, 0, seeThrough); - } - - public static void renderText(WorldRenderContext context, Text text, Vec3d pos, float scale, float yOffset, boolean seeThrough) { - renderText(context, text.asOrderedText(), pos, scale, yOffset, seeThrough); - } - - /** - * Renders text in the world space. - * - * @param seeThrough Whether the text should be able to be seen through walls or not. - */ - public static void renderText(WorldRenderContext context, OrderedText text, Vec3d pos, float scale, float yOffset, boolean seeThrough) { - MatrixStack matrices = context.matrixStack(); - Vec3d camera = context.camera().getPos(); - TextRenderer textRenderer = client.textRenderer; - - scale *= 0.025f; - - matrices.push(); - matrices.translate(pos.getX() - camera.getX(), pos.getY() - camera.getY(), pos.getZ() - camera.getZ()); - matrices.peek().getPositionMatrix().mul(RenderSystem.getModelViewMatrix()); - matrices.multiply(context.camera().getRotation()); - matrices.scale(-scale, -scale, scale); - - Matrix4f positionMatrix = matrices.peek().getPositionMatrix(); - float xOffset = -textRenderer.getWidth(text) / 2f; - - Tessellator tessellator = RenderSystem.renderThreadTesselator(); - BufferBuilder buffer = tessellator.getBuffer(); - VertexConsumerProvider.Immediate consumers = VertexConsumerProvider.immediate(buffer); - - RenderSystem.depthFunc(seeThrough ? GL11.GL_ALWAYS : GL11.GL_LEQUAL); - - textRenderer.draw(text, xOffset, yOffset, 0xFFFFFFFF, false, positionMatrix, consumers, TextRenderer.TextLayerType.SEE_THROUGH, 0, LightmapTextureManager.MAX_LIGHT_COORDINATE); - consumers.draw(); - - RenderSystem.depthFunc(GL11.GL_LEQUAL); - matrices.pop(); - } - - /** - * Adds the title to {@link TitleContainer} and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already. - * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller. - * - * @param title the title - */ - public static void displayInTitleContainerAndPlaySound(Title title) { - if (TitleContainer.addTitle(title)) { - playNotificationSound(); - } - } - - /** - * Adds the title to {@link TitleContainer} for a set number of ticks and {@link #playNotificationSound() plays the notification sound} if the title is not in the {@link TitleContainer} already. - * No checking needs to be done on whether the title is in the {@link TitleContainer} already by the caller. - * - * @param title the title - * @param ticks the number of ticks the title will remain - */ - public static void displayInTitleContainerAndPlaySound(Title title, int ticks) { - if (TitleContainer.addTitle(title, ticks)) { - playNotificationSound(); - } - } - - private static void playNotificationSound() { - if (MinecraftClient.getInstance().player != null) { - MinecraftClient.getInstance().player.playSound(SoundEvents.ENTITY_EXPERIENCE_ORB_PICKUP, 100f, 0.1f); - } - } - - public static boolean pointIsInArea(double x, double y, double x1, double y1, double x2, double y2) { - return x >= x1 && x <= x2 && y >= y1 && y <= y2; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/OcclusionCulling.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/OcclusionCulling.java deleted file mode 100644 index 5c7f69ad..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/OcclusionCulling.java +++ /dev/null @@ -1,47 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.culling; - -import com.logisticscraft.occlusionculling.OcclusionCullingInstance; -import com.logisticscraft.occlusionculling.cache.ArrayOcclusionCache; -import com.logisticscraft.occlusionculling.util.Vec3d; -import me.xmrvizzy.skyblocker.utils.render.FrustumUtils; -import net.minecraft.client.MinecraftClient; - -public class OcclusionCulling { - private static final int TRACING_DISTANCE = 128; - private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); - private static OcclusionCullingInstance instance = null; - - // Reused objects to reduce allocation overhead - private static final Vec3d cameraPos = new Vec3d(0, 0, 0); - private static final Vec3d min = new Vec3d(0, 0, 0); - private static final Vec3d max = new Vec3d(0, 0, 0); - - /** - * Initializes the occlusion culling instance - */ - public static void init() { - instance = new OcclusionCullingInstance(TRACING_DISTANCE, new WorldProvider(), new ArrayOcclusionCache(TRACING_DISTANCE), 2); - } - - private static void updateCameraPos() { - var camera = CLIENT.gameRenderer.getCamera().getPos(); - cameraPos.set(camera.x, camera.y, camera.z); - } - - /** - * This first checks checks if the bounding box is within the camera's FOV, if - * it is then it checks for whether it's occluded or not. - * - * @return A boolean representing whether the bounding box is fully visible or - * not. - */ - public static boolean isVisible(double x1, double y1, double z1, double x2, double y2, double z2) { - if (!FrustumUtils.isVisible(x1, y1, z1, x2, y2, z2)) return false; - - updateCameraPos(); - min.set(x1, y1, z1); - max.set(x2, y2, z2); - - return instance.isAABBVisible(min, max, cameraPos); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/WorldProvider.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/WorldProvider.java deleted file mode 100644 index 09a7bc79..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/WorldProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.culling; - -import com.logisticscraft.occlusionculling.DataProvider; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.util.math.BlockPos; - -public class WorldProvider implements DataProvider { - private final static MinecraftClient CLIENT = MinecraftClient.getInstance(); - private ClientWorld world = null; - - @Override - public boolean prepareChunk(int chunkX, int chunkZ) { - this.world = CLIENT.world; - return this.world != null; - } - - @Override - public boolean isOpaqueFullCube(int x, int y, int z) { - BlockPos pos = new BlockPos(x, y, z); - return this.world.getBlockState(pos).isOpaqueFullCube(this.world, pos); - } - - @Override - public void cleanup() { - this.world = null; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/package-info.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/package-info.java deleted file mode 100644 index 97ebac86..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/culling/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Package dedicated to occlusion culling utilities - */ -package me.xmrvizzy.skyblocker.utils.render.culling; \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ColorHighlight.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ColorHighlight.java deleted file mode 100644 index 41cd0778..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ColorHighlight.java +++ /dev/null @@ -1,24 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.gui; - -public record ColorHighlight(int slot, int color) { - private static final int RED_HIGHLIGHT = 64 << 24 | 255 << 16; - private static final int YELLOW_HIGHLIGHT = 128 << 24 | 255 << 16 | 255 << 8; - private static final int GREEN_HIGHLIGHT = 128 << 24 | 64 << 16 | 196 << 8 | 64; - private static final int GRAY_HIGHLIGHT = 128 << 24 | 64 << 16 | 64 << 8 | 64; - - public static ColorHighlight red(int slot) { - return new ColorHighlight(slot, RED_HIGHLIGHT); - } - - public static ColorHighlight yellow(int slot) { - return new ColorHighlight(slot, YELLOW_HIGHLIGHT); - } - - public static ColorHighlight green(int slot) { - return new ColorHighlight(slot, GREEN_HIGHLIGHT); - } - - public static ColorHighlight gray(int slot) { - return new ColorHighlight(slot, GRAY_HIGHLIGHT); - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolver.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolver.java deleted file mode 100644 index 83c0f717..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolver.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.gui; - -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.item.ItemStack; - -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; - -/** - * Abstract class for gui solvers. Extend this class to add a new gui solver, like terminal solvers or experiment solvers. - */ -public abstract class ContainerSolver { - private final Pattern containerName; - - protected ContainerSolver(String containerName) { - this.containerName = Pattern.compile(containerName); - } - - protected abstract boolean isEnabled(); - - public Pattern getName() { - return containerName; - } - - protected void start(GenericContainerScreen screen) { - } - - protected void reset() { - } - - protected abstract List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots); - - protected void trimEdges(Map<Integer, ItemStack> slots, int rows) { - for (int i = 0; i < rows; i++) { - slots.remove(9 * i); - slots.remove(9 * i + 8); - } - for (int i = 1; i < 8; i++) { - slots.remove(i); - slots.remove((rows - 1) * 9 + i); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolverManager.java deleted file mode 100644 index f78222d0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/gui/ContainerSolverManager.java +++ /dev/null @@ -1,125 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.gui; - -import com.mojang.blaze3d.systems.RenderSystem; -import me.xmrvizzy.skyblocker.mixin.accessor.HandledScreenAccessor; -import me.xmrvizzy.skyblocker.skyblock.dungeon.CroesusHelper; -import me.xmrvizzy.skyblocker.skyblock.dungeon.terminal.ColorTerminal; -import me.xmrvizzy.skyblocker.skyblock.dungeon.terminal.OrderTerminal; -import me.xmrvizzy.skyblocker.skyblock.dungeon.terminal.StartsWithTerminal; -import me.xmrvizzy.skyblocker.skyblock.experiment.ChronomatronSolver; -import me.xmrvizzy.skyblocker.skyblock.experiment.SuperpairsSolver; -import me.xmrvizzy.skyblocker.skyblock.experiment.UltrasequencerSolver; -import me.xmrvizzy.skyblocker.utils.Utils; -import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.item.ItemStack; -import net.minecraft.screen.slot.Slot; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Manager class for {@link ContainerSolver}s like terminal solvers and experiment solvers. To add a new gui solver, extend {@link ContainerSolver} and register it in {@link #ContainerSolverManager()}. - */ -public class ContainerSolverManager { - private static final Pattern PLACEHOLDER_PATTERN = Pattern.compile(""); - private final ContainerSolver[] solvers; - private ContainerSolver currentSolver = null; - private String[] groups; - private List<ColorHighlight> highlights; - - public ContainerSolverManager() { - solvers = new ContainerSolver[]{ - new ColorTerminal(), - new OrderTerminal(), - new StartsWithTerminal(), - new CroesusHelper(), - new ChronomatronSolver(), - new SuperpairsSolver(), - new UltrasequencerSolver() - }; - } - - public ContainerSolver getCurrentSolver() { - return currentSolver; - } - - public void init() { - ScreenEvents.BEFORE_INIT.register((client, screen, scaledWidth, scaledHeight) -> { - if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) { - ScreenEvents.afterRender(screen).register((screen1, context, mouseX, mouseY, delta) -> { - MatrixStack matrices = context.getMatrices(); - matrices.push(); - matrices.translate(((HandledScreenAccessor) genericContainerScreen).getX(), ((HandledScreenAccessor) genericContainerScreen).getY(), 300); - onDraw(context, genericContainerScreen.getScreenHandler().slots.subList(0, genericContainerScreen.getScreenHandler().getRows() * 9)); - matrices.pop(); - }); - ScreenEvents.remove(screen).register(screen1 -> clearScreen()); - onSetScreen(genericContainerScreen); - } else { - clearScreen(); - } - }); - } - - public void onSetScreen(@NotNull GenericContainerScreen screen) { - String screenName = screen.getTitle().getString(); - Matcher matcher = PLACEHOLDER_PATTERN.matcher(screenName); - for (ContainerSolver solver : solvers) { - if (solver.isEnabled()) { - matcher.usePattern(solver.getName()); - matcher.reset(); - if (matcher.matches()) { - currentSolver = solver; - groups = new String[matcher.groupCount()]; - for (int i = 0; i < groups.length; i++) { - groups[i] = matcher.group(i + 1); - } - currentSolver.start(screen); - return; - } - } - } - clearScreen(); - } - - public void clearScreen() { - if (currentSolver != null) { - currentSolver.reset(); - currentSolver = null; - } - } - - public void markDirty() { - highlights = null; - } - - public void onDraw(DrawContext context, List<Slot> slots) { - if (currentSolver == null) - return; - if (highlights == null) - highlights = currentSolver.getColors(groups, slotMap(slots)); - RenderSystem.enableDepthTest(); - RenderSystem.colorMask(true, true, true, false); - for (ColorHighlight highlight : highlights) { - Slot slot = slots.get(highlight.slot()); - int color = highlight.color(); - context.fillGradient(slot.x, slot.y, slot.x + 16, slot.y + 16, color, color); - } - RenderSystem.colorMask(true, true, true, true); - } - - private Map<Integer, ItemStack> slotMap(List<Slot> slots) { - Map<Integer, ItemStack> slotMap = new TreeMap<>(); - for (int i = 0; i < slots.size(); i++) { - slotMap.put(i, slots.get(i).getStack()); - } - return slotMap; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/Title.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/Title.java deleted file mode 100644 index b48771ee..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/Title.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.title; - -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -/** - * Represents a title used for {@link TitleContainer}. - * - * @see TitleContainer - */ -public class Title { - private MutableText text; - protected float x = -1; - protected float y = -1; - - /** - * Constructs a new title with the given translation key and formatting to be applied. - * - * @param textKey the translation key - * @param formatting the formatting to be applied to the text - */ - public Title(String textKey, Formatting formatting) { - this(Text.translatable(textKey).formatted(formatting)); - } - - /** - * Constructs a new title with the given {@link MutableText}. - * Use {@link Text#literal(String)} or {@link Text#translatable(String)} to create a {@link MutableText} - * - * @param text the mutable text - */ - public Title(MutableText text) { - this.text = text; - } - - public MutableText getText() { - return text; - } - - public void setText(MutableText text) { - this.text = text; - } - - protected boolean isDefaultPos() { - return x == -1 && y == -1; - } - - protected void resetPos() { - this.x = -1; - this.y = -1; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java deleted file mode 100644 index a55965dc..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainer.java +++ /dev/null @@ -1,175 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.title; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; -import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; -import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.util.math.MathHelper; - -import java.util.LinkedHashSet; -import java.util.Set; - -public class TitleContainer { - /** - * The set of titles which will be rendered. - * - * @see #containsTitle(Title) - * @see #addTitle(Title) - * @see #addTitle(Title, int) - * @see #removeTitle(Title) - */ - private static final Set<Title> titles = new LinkedHashSet<>(); - - public static void init() { - HudRenderCallback.EVENT.register(TitleContainer::render); - ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker") - .then(ClientCommandManager.literal("hud") - .then(ClientCommandManager.literal("titleContainer") - .executes(Scheduler.queueOpenScreenCommand(TitleContainerConfigScreen::new)))))); - } - - /** - * Returns {@code true} if the title is currently shown. - * - * @param title the title to check - * @return whether the title in currently shown - */ - public static boolean containsTitle(Title title) { - return titles.contains(title); - } - - /** - * Adds a title to be shown - * - * @param title the title to be shown - * @return whether the title is already currently being shown - */ - public static boolean addTitle(Title title) { - if (titles.add(title)) { - title.resetPos(); - return true; - } - return false; - } - - /** - * Adds a title to be shown for a set number of ticks - * - * @param title the title to be shown - * @param ticks the number of ticks to show the title - * @return whether the title is already currently being shown - */ - public static boolean addTitle(Title title, int ticks) { - if (addTitle(title)) { - Scheduler.INSTANCE.schedule(() -> TitleContainer.removeTitle(title), ticks); - return true; - } - return false; - } - - /** - * Stops showing a title - * - * @param title the title to stop showing - */ - public static void removeTitle(Title title) { - titles.remove(title); - } - - private static void render(DrawContext context, float tickDelta) { - render(context, titles, SkyblockerConfigManager.get().general.titleContainer.x, SkyblockerConfigManager.get().general.titleContainer.y, tickDelta); - } - - protected static void render(DrawContext context, Set<Title> titles, int xPos, int yPos, float tickDelta) { - var client = MinecraftClient.getInstance(); - TextRenderer textRenderer = client.textRenderer; - - // Calculate Scale to use - float scale = 3F * (SkyblockerConfigManager.get().general.titleContainer.titleContainerScale / 100F); - - // Grab direction and alignment values - SkyblockerConfig.Direction direction = SkyblockerConfigManager.get().general.titleContainer.direction; - SkyblockerConfig.Alignment alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; - // x/y refer to the starting position for the text - // y always starts at yPos - float x = 0; - float y = yPos; - - //Calculate the width of combined text - float width = 0; - for (Title title : titles) { - width += textRenderer.getWidth(title.getText()) * scale + 10; - } - - if (alignment == SkyblockerConfig.Alignment.MIDDLE) { - if (direction == SkyblockerConfig.Direction.HORIZONTAL) { - //If middle aligned horizontally, start the xPosition at half of the width to the left. - x = xPos - (width / 2); - } else { - //If middle aligned vertically, start at xPos, we will shift each text to the left later - x = xPos; - } - } - if (alignment == SkyblockerConfig.Alignment.LEFT || alignment == SkyblockerConfig.Alignment.RIGHT) { - //If left or right aligned, start at xPos, we will shift each text later - x = xPos; - } - - for (Title title : titles) { - - //Calculate which x the text should use - float xToUse; - if (direction == SkyblockerConfig.Direction.HORIZONTAL) { - xToUse = alignment == SkyblockerConfig.Alignment.RIGHT ? - x - (textRenderer.getWidth(title.getText()) * scale) : //if right aligned we need the text position to be aligned on the right side. - x; - } else { - xToUse = alignment == SkyblockerConfig.Alignment.MIDDLE ? - x - (textRenderer.getWidth(title.getText()) * scale) / 2 : //if middle aligned we need the text position to be aligned in the middle. - alignment == SkyblockerConfig.Alignment.RIGHT ? - x - (textRenderer.getWidth(title.getText()) * scale) : //if right aligned we need the text position to be aligned on the right side. - x; - } - - //Start displaying the title at the correct position, not at the default position - if (title.isDefaultPos()) { - title.x = xToUse; - title.y = y; - } - - //Lerp the texts x and y variables - title.x = MathHelper.lerp(tickDelta * 0.5F, title.x, xToUse); - title.y = MathHelper.lerp(tickDelta * 0.5F, title.y, y); - - //Translate the matrix to the texts position and scale - context.getMatrices().push(); - context.getMatrices().translate(title.x, title.y, 200); - context.getMatrices().scale(scale, scale, scale); - - //Draw text - context.drawTextWithShadow(textRenderer, title.getText(), 0, 0, 0xFFFFFF); - context.getMatrices().pop(); - - //Calculate the x and y positions for the next title - if (direction == SkyblockerConfig.Direction.HORIZONTAL) { - if (alignment == SkyblockerConfig.Alignment.MIDDLE || alignment == SkyblockerConfig.Alignment.LEFT) { - //Move to the right if middle or left aligned - x += textRenderer.getWidth(title.getText()) * scale + 10; - } - - if (alignment == SkyblockerConfig.Alignment.RIGHT) { - //Move to the left if right aligned - x -= textRenderer.getWidth(title.getText()) * scale + 10; - } - } else { - //Y always moves by the same amount if vertical - y += textRenderer.fontHeight * scale + 10; - } - } - } -} \ No newline at end of file diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainerConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainerConfigScreen.java deleted file mode 100644 index caf9fbf0..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/render/title/TitleContainerConfigScreen.java +++ /dev/null @@ -1,170 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.render.title; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.render.RenderHelper; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.util.math.Vector2f; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import net.minecraft.util.Pair; -import org.lwjgl.glfw.GLFW; - -import java.awt.*; -import java.util.Set; - -public class TitleContainerConfigScreen extends Screen { - private final Title example1 = new Title(Text.literal("Test1").formatted(Formatting.RED)); - private final Title example2 = new Title(Text.literal("Test23").formatted(Formatting.AQUA)); - private final Title example3 = new Title(Text.literal("Testing1234").formatted(Formatting.DARK_GREEN)); - private float hudX = SkyblockerConfigManager.get().general.titleContainer.x; - private float hudY = SkyblockerConfigManager.get().general.titleContainer.y; - private final Screen parent; - - protected TitleContainerConfigScreen() { - this(null); - } - - public TitleContainerConfigScreen(Screen parent) { - super(Text.of("Title Container HUD Config")); - this.parent = parent; - } - - @Override - public void render(DrawContext context, int mouseX, int mouseY, float delta) { - super.render(context, mouseX, mouseY, delta); - renderBackground(context, mouseX, mouseY, delta); - TitleContainer.render(context, Set.of(example1, example2, example3), (int) hudX, (int) hudY, delta); - SkyblockerConfig.Direction direction = SkyblockerConfigManager.get().general.titleContainer.direction; - SkyblockerConfig.Alignment alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; - context.drawCenteredTextWithShadow(textRenderer, "Press Q/E to change Alignment: " + alignment, width / 2, textRenderer.fontHeight * 2, Color.WHITE.getRGB()); - context.drawCenteredTextWithShadow(textRenderer, "Press R to change Direction: " + direction, width / 2, textRenderer.fontHeight * 3 + 5, Color.WHITE.getRGB()); - context.drawCenteredTextWithShadow(textRenderer, "Press +/- to change Scale", width / 2, textRenderer.fontHeight * 4 + 10, Color.WHITE.getRGB()); - context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width / 2, textRenderer.fontHeight * 5 + 15, Color.GRAY.getRGB()); - - Pair<Vector2f, Vector2f> boundingBox = getSelectionBoundingBox(); - int x1 = (int) boundingBox.getLeft().getX(); - int y1 = (int) boundingBox.getLeft().getY(); - int x2 = (int) boundingBox.getRight().getX(); - int y2 = (int) boundingBox.getRight().getY(); - - context.drawHorizontalLine(x1, x2, y1, Color.RED.getRGB()); - context.drawHorizontalLine(x1, x2, y2, Color.RED.getRGB()); - context.drawVerticalLine(x1, y1, y2, Color.RED.getRGB()); - context.drawVerticalLine(x2, y1, y2, Color.RED.getRGB()); - } - - private Pair<Vector2f, Vector2f> getSelectionBoundingBox() { - SkyblockerConfig.Alignment alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; - - float midWidth = getSelectionWidth() / 2F; - float x1 = 0; - float x2 = 0; - float y1 = hudY; - float y2 = hudY + getSelectionHeight(); - switch (alignment) { - case RIGHT -> { - x1 = hudX - midWidth * 2; - x2 = hudX; - } - case MIDDLE -> { - x1 = hudX - midWidth; - x2 = hudX + midWidth; - } - case LEFT -> { - x1 = hudX; - x2 = hudX + midWidth * 2; - } - } - return new Pair<>(new Vector2f(x1, y1), new Vector2f(x2, y2)); - } - - private float getSelectionHeight() { - float scale = (3F * (SkyblockerConfigManager.get().general.titleContainer.titleContainerScale / 100F)); - return SkyblockerConfigManager.get().general.titleContainer.direction == SkyblockerConfig.Direction.HORIZONTAL ? - (textRenderer.fontHeight * scale) : - (textRenderer.fontHeight + 10F) * 3F * scale; - } - - private float getSelectionWidth() { - float scale = (3F * (SkyblockerConfigManager.get().general.titleContainer.titleContainerScale / 100F)); - return SkyblockerConfigManager.get().general.titleContainer.direction == SkyblockerConfig.Direction.HORIZONTAL ? - (textRenderer.getWidth("Test1") + 10 + textRenderer.getWidth("Test23") + 10 + textRenderer.getWidth("Testing1234")) * scale : - textRenderer.getWidth("Testing1234") * scale; - } - - @Override - public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) { - float midWidth = getSelectionWidth() / 2; - float midHeight = getSelectionHeight() / 2; - var alignment = SkyblockerConfigManager.get().general.titleContainer.alignment; - - Pair<Vector2f, Vector2f> boundingBox = getSelectionBoundingBox(); - float x1 = boundingBox.getLeft().getX(); - float y1 = boundingBox.getLeft().getY(); - float x2 = boundingBox.getRight().getX(); - float y2 = boundingBox.getRight().getY(); - - if (RenderHelper.pointIsInArea(mouseX, mouseY, x1, y1, x2, y2) && button == 0) { - hudX = switch (alignment) { - case LEFT -> (int) mouseX - midWidth; - case MIDDLE -> (int) mouseX; - case RIGHT -> (int) mouseX + midWidth; - }; - hudY = (int) (mouseY - midHeight); - } - return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY); - } - - @Override - public boolean mouseClicked(double mouseX, double mouseY, int button) { - if (button == 1) { - hudX = (float) this.width / 2; - hudY = this.height * 0.6F; - } - return super.mouseClicked(mouseX, mouseY, button); - } - - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (keyCode == GLFW.GLFW_KEY_Q) { - SkyblockerConfig.Alignment current = SkyblockerConfigManager.get().general.titleContainer.alignment; - SkyblockerConfigManager.get().general.titleContainer.alignment = switch (current) { - case LEFT -> SkyblockerConfig.Alignment.MIDDLE; - case MIDDLE -> SkyblockerConfig.Alignment.RIGHT; - case RIGHT -> SkyblockerConfig.Alignment.LEFT; - }; - } - if (keyCode == GLFW.GLFW_KEY_E) { - SkyblockerConfig.Alignment current = SkyblockerConfigManager.get().general.titleContainer.alignment; - SkyblockerConfigManager.get().general.titleContainer.alignment = switch (current) { - case LEFT -> SkyblockerConfig.Alignment.RIGHT; - case MIDDLE -> SkyblockerConfig.Alignment.LEFT; - case RIGHT -> SkyblockerConfig.Alignment.MIDDLE; - }; - } - if (keyCode == GLFW.GLFW_KEY_R) { - SkyblockerConfig.Direction current = SkyblockerConfigManager.get().general.titleContainer.direction; - SkyblockerConfigManager.get().general.titleContainer.direction = switch (current) { - case HORIZONTAL -> SkyblockerConfig.Direction.VERTICAL; - case VERTICAL -> SkyblockerConfig.Direction.HORIZONTAL; - }; - } - if (keyCode == GLFW.GLFW_KEY_EQUAL) { - SkyblockerConfigManager.get().general.titleContainer.titleContainerScale += 10; - } - if (keyCode == GLFW.GLFW_KEY_MINUS) { - SkyblockerConfigManager.get().general.titleContainer.titleContainerScale -= 10; - } - return super.keyPressed(keyCode, scanCode, modifiers); - } - - @Override - public void close() { - SkyblockerConfigManager.get().general.titleContainer.x = (int) hudX; - SkyblockerConfigManager.get().general.titleContainer.y = (int) hudY; - SkyblockerConfigManager.save(); - this.client.setScreen(parent); - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java deleted file mode 100644 index b8ffa548..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/MessageScheduler.java +++ /dev/null @@ -1,66 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.scheduler; - -import net.minecraft.client.MinecraftClient; - -/** - * A scheduler for sending chat messages or commands. Use the instance in {@link #INSTANCE}. Do not instantiate this class. - */ -public class MessageScheduler extends Scheduler { - /** - * The minimum delay that the server will accept between chat messages. - */ - private static final int MIN_DELAY = 200; - public static final MessageScheduler INSTANCE = new MessageScheduler(); - /** - * The timestamp of the last message send, - */ - private long lastMessage = 0; - - protected MessageScheduler() { - } - - /** - * Sends a chat message or command after the minimum cooldown. Prefer this method to send messages or commands to the server. - * - * @param message the message to send - */ - public void sendMessageAfterCooldown(String message) { - if (lastMessage + MIN_DELAY < System.currentTimeMillis()) { - sendMessage(message); - lastMessage = System.currentTimeMillis(); - } else { - queueMessage(message, 0); - } - } - - private void sendMessage(String message) { - if (MinecraftClient.getInstance().player != null) { - if (message.startsWith("/")) { - MinecraftClient.getInstance().player.networkHandler.sendCommand(message.substring(1)); - } else { - MinecraftClient.getInstance().inGameHud.getChatHud().addToMessageHistory(message); - MinecraftClient.getInstance().player.networkHandler.sendChatMessage(message); - } - } - } - - /** - * Queues a chat message or command to send in {@code delay} ticks. Use this method to send messages or commands a set time in the future. The minimum cooldown is still respected. - * - * @param message the message to send - * @param delay the delay before sending the message in ticks - */ - public void queueMessage(String message, int delay) { - schedule(() -> sendMessage(message), delay); - } - - @Override - protected boolean runTask(Runnable task) { - if (lastMessage + MIN_DELAY < System.currentTimeMillis()) { - task.run(); - lastMessage = System.currentTimeMillis(); - return true; - } - return false; - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java b/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java deleted file mode 100644 index 700bdce3..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/scheduler/Scheduler.java +++ /dev/null @@ -1,140 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.scheduler; - -import com.mojang.brigadier.Command; -import it.unimi.dsi.fastutil.ints.AbstractInt2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Supplier; - -/** - * A scheduler for running tasks at a later time. Tasks will be run synchronously on the main client thread. Use the instance stored in {@link #INSTANCE}. Do not instantiate this class. - */ -public class Scheduler { - private static final Logger LOGGER = LoggerFactory.getLogger(Scheduler.class); - public static final Scheduler INSTANCE = new Scheduler(); - private int currentTick = 0; - private final AbstractInt2ObjectMap<List<ScheduledTask>> tasks = new Int2ObjectOpenHashMap<>(); - - protected Scheduler() { - } - - /** - * Schedules a task to run after a delay. - * - * @param task the task to run - * @param delay the delay in ticks - */ - public void schedule(Runnable task, int delay) { - if (delay >= 0) { - addTask(new ScheduledTask(task), currentTick + delay); - } else { - LOGGER.warn("Scheduled a task with negative delay"); - } - } - - /** - * Schedules a task to run every period ticks. - * - * @param task the task to run - * @param period the period in ticks - */ - public void scheduleCyclic(Runnable task, int period) { - if (period > 0) { - addTask(new CyclicTask(task, period), currentTick); - } else { - LOGGER.error("Attempted to schedule a cyclic task with period lower than 1"); - } - } - - public static Command<FabricClientCommandSource> queueOpenScreenCommand(Supplier<Screen> screenSupplier) { - return context -> INSTANCE.queueOpenScreen(screenSupplier); - } - - /** - * Schedules a screen to open in the next tick. Used in commands to avoid screen immediately closing after the command is executed. - * - * @param screenSupplier the supplier of the screen to open - * @see #queueOpenScreenCommand(Supplier) - */ - public int queueOpenScreen(Supplier<Screen> screenSupplier) { - MinecraftClient.getInstance().send(() -> MinecraftClient.getInstance().setScreen(screenSupplier.get())); - return Command.SINGLE_SUCCESS; - } - - public void tick() { - if (tasks.containsKey(currentTick)) { - List<ScheduledTask> currentTickTasks = tasks.get(currentTick); - //noinspection ForLoopReplaceableByForEach (or else we get a ConcurrentModificationException) - for (int i = 0; i < currentTickTasks.size(); i++) { - ScheduledTask task = currentTickTasks.get(i); - if (!runTask(task)) { - tasks.computeIfAbsent(currentTick + 1, key -> new ArrayList<>()).add(task); - } - } - tasks.remove(currentTick); - } - currentTick += 1; - } - - /** - * Runs the task if able. - * - * @param task the task to run - * @return {@code true} if the task is run, and {@link false} if task is not run. - */ - protected boolean runTask(Runnable task) { - task.run(); - return true; - } - - private void addTask(ScheduledTask scheduledTask, int schedule) { - if (tasks.containsKey(schedule)) { - tasks.get(schedule).add(scheduledTask); - } else { - List<ScheduledTask> list = new ArrayList<>(); - list.add(scheduledTask); - tasks.put(schedule, list); - } - } - - /** - * A task that runs every period ticks. More specifically, this task reschedules itself to run again after period ticks every time it runs. - */ - protected class CyclicTask extends ScheduledTask { - private final int period; - - CyclicTask(Runnable inner, int period) { - super(inner); - this.period = period; - } - - @Override - public void run() { - super.run(); - addTask(this, currentTick + period); - } - } - - /** - * A task that runs at a specific tick, relative to {@link #currentTick}. - */ - protected static class ScheduledTask implements Runnable { - private final Runnable inner; - - public ScheduledTask(Runnable inner) { - this.inner = inner; - } - - @Override - public void run() { - inner.run(); - } - } -} diff --git a/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java b/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java deleted file mode 100644 index 5e5c8f89..00000000 --- a/src/main/java/me/xmrvizzy/skyblocker/utils/tictactoe/TicTacToeUtils.java +++ /dev/null @@ -1,104 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.tictactoe; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public class TicTacToeUtils { - - public static int getBestMove(char[][] board) { - HashMap<Integer, Integer> moves = new HashMap<>(); - for (int row = 0; row < board.length; row++) { - for (int col = 0; col < board[row].length; col++) { - if (board[row][col] != '\0') continue; - board[row][col] = 'O'; - int score = alphabeta(board, Integer.MIN_VALUE, Integer.MAX_VALUE, false, 0); - board[row][col] = '\0'; - moves.put(row * 3 + col + 1, score); - } - } - return Collections.max(moves.entrySet(), Map.Entry.comparingByValue()).getKey(); - } - - public static boolean hasMovesLeft(char[][] board) { - for (char[] rows : board) { - for (char col : rows) { - if (col == '\0') return true; - } - } - return false; - } - - public static int getBoardRanking(char[][] board) { - for (int row = 0; row < 3; row++) { - if (board[row][0] == board[row][1] && board[row][0] == board[row][2]) { - if (board[row][0] == 'X') { - return -10; - } else if (board[row][0] == 'O') { - return 10; - } - } - } - - for (int col = 0; col < 3; col++) { - if (board[0][col] == board[1][col] && board[0][col] == board[2][col]) { - if (board[0][col] == 'X') { - return -10; - } else if (board[0][col] == 'O') { - return 10; - } - } - } - - if (board[0][0] == board[1][1] && board[0][0] == board[2][2]) { - if (board[0][0] == 'X') { - return -10; - } else if (board[0][0] == 'O') { - return 10; - } - } else if (board[0][2] == board[1][1] && board[0][2] == board[2][0]) { - if (board[0][2] == 'X') { - return -10; - } else if (board[0][2] == 'O') { - return 10; - } - } - - return 0; - } - public static int alphabeta(char[][] board, int alpha, int beta, boolean max, int depth) { - int score = getBoardRanking(board); - if (score == 10 || score == -10) return score; - if (!hasMovesLeft(board)) return 0; - - if (max) { - int bestScore = Integer.MIN_VALUE; - for (int row = 0; row < 3; row++) { - for (int col = 0; col < 3; col++) { - if (board[row][col] == '\0') { - board[row][col] = 'O'; - bestScore = Math.max(bestScore, alphabeta(board, alpha, beta, false, depth + 1)); - board[row][col] = '\0'; - alpha = Math.max(alpha, bestScore); - if (beta <= alpha) break; // Pruning - } - } - } - return bestScore - depth; - } else { - int bestScore = Integer.MAX_VALUE; - for (int row = 0; row < 3; row++) { - for (int col = 0; col < 3; col++) { - if (board[row][col] == '\0') { - board[row][col] = 'X'; - bestScore = Math.min(bestScore, alphabeta(board, alpha, beta, true, depth + 1)); - board[row][col] = '\0'; - beta = Math.min(beta, bestScore); - if (beta <= alpha) break; // Pruning - } - } - } - return bestScore + depth; - } - } -} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index e9dc5e58..86481bf3 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -16,19 +16,19 @@ "environment": "client", "entrypoints": { "client": [ - "me.xmrvizzy.skyblocker.SkyblockerMod" + "de.hysky.skyblocker.SkyblockerMod" ], "preLaunch": [ "dev.cbyrne.betterinject.BetterInject::initialize" ], "modmenu": [ - "me.xmrvizzy.skyblocker.compatibility.modmenu.ModMenuEntry" + "de.hysky.skyblocker.compatibility.modmenu.ModMenuEntry" ], "rei_client": [ - "me.xmrvizzy.skyblocker.compatibility.rei.SkyblockerREIClientPlugin" + "de.hysky.skyblocker.compatibility.rei.SkyblockerREIClientPlugin" ], "emi": [ - "me.xmrvizzy.skyblocker.compatibility.emi.SkyblockerEMIPlugin" + "de.hysky.skyblocker.compatibility.emi.SkyblockerEMIPlugin" ] }, "mixins": [ diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 4afb2bbd..efca46f7 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -1,7 +1,7 @@ { "required": true, - "package": "me.xmrvizzy.skyblocker.mixin", - "plugin": "me.xmrvizzy.skyblocker.compatibility.MixinPlugin", + "package": "de.hysky.skyblocker.mixin", + "plugin": "de.hysky.skyblocker.compatibility.MixinPlugin", "compatibilityLevel": "JAVA_17", "client": [ "AbstractInventoryScreenMixin", diff --git a/src/test/java/de/hysky/skyblocker/skyblock/StatusBarTrackerTest.java b/src/test/java/de/hysky/skyblocker/skyblock/StatusBarTrackerTest.java new file mode 100644 index 00000000..c058da5d --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/StatusBarTrackerTest.java @@ -0,0 +1,74 @@ +package de.hysky.skyblocker.skyblock; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class StatusBarTrackerTest { + private StatusBarTracker tracker; + + @BeforeEach + void setUp() { + tracker = new StatusBarTracker(); + } + + void assertStats(int hp, int maxHp, int def, int mana, int maxMana, int overflowMana) { + int absorption = 0; + if(hp > maxHp) { + absorption = hp - maxHp; + hp -= absorption; + if(absorption > maxHp) + absorption = maxHp; + } + assertEquals(new StatusBarTracker.Resource(hp, maxHp, absorption), tracker.getHealth()); + assertEquals(new StatusBarTracker.Resource(mana, maxMana, overflowMana), tracker.getMana()); + } + + @Test + void normalStatusBar() { + String res = tracker.update("§c934/1086❤ §a159§a❈ Defense §b562/516✎ Mana", false); + assertNull(res); + assertStats(934, 1086, 159, 562, 516, 0); + } + + @Test + void overflowMana() { + String res = tracker.update("§61605/1305❤ §a270§a❈ Defense §b548/548✎ §3200ʬ", false); + assertNull(res); + assertStats(1605, 1305, 270, 548, 548, 200); + } + + @Test + void regeneration() { + String res = tracker.update("§c2484/2484❤+§c120▄ §a642§a❈ Defense §b2557/2611✎ Mana", false); + assertEquals("§c❤+§c120▄", res); + } + + @Test + void instantTransmission() { + String actionBar = "§c2259/2259❤ §b-20 Mana (§6Instant Transmission§b) §b549/2676✎ Mana"; + assertEquals("§b-20 Mana (§6Instant Transmission§b)", tracker.update(actionBar, false)); + assertNull(tracker.update(actionBar, true)); + } + + @Test + void rapidFire() { + String actionBar = "§c2509/2509❤ §b-48 Mana (§6Rapid-fire§b) §b2739/2811✎ Mana"; + assertEquals("§b-48 Mana (§6Rapid-fire§b)", tracker.update(actionBar, false)); + assertNull(tracker.update(actionBar, true)); + } + + @Test + void zombieSword() { + String actionBar = "§c2509/2509❤ §b-56 Mana (§6Instant Heal§b) §b2674/2821✎ Mana §e§lⓩⓩⓩⓩ§6§lⓄ"; + assertEquals("§b-56 Mana (§6Instant Heal§b) §e§lⓩⓩⓩⓩ§6§lⓄ", tracker.update(actionBar, false)); + assertEquals("§e§lⓩⓩⓩⓩ§6§lⓄ", tracker.update(actionBar, true)); + } + + @Test + void campfire() { + String res = tracker.update("§c17070/25565❤+§c170▃ §65,625 DPS §c1 second §b590/626✎ §3106ʬ", false); + assertEquals("§c❤+§c170▃ §65,625 DPS §c1 second", res); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/AcceptRepartyTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/AcceptRepartyTest.java new file mode 100644 index 00000000..5dba3f96 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/AcceptRepartyTest.java @@ -0,0 +1,37 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +import java.util.regex.Matcher; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class AcceptRepartyTest extends ChatPatternListenerTest<Reparty> { + + public AcceptRepartyTest() { super(new Reparty()); } + + protected void assertGroup(String message, String group, String expect) { + Matcher matcher = matcher(message); + assertTrue(matcher.matches()); + assertEquals(expect, matcher.group(group)); + } + + @Test + void testDisband() { + assertGroup("[VIP+] KoloiYolo has disbanded the party!", + /* group: */ "disband", + /* expect: */ "KoloiYolo"); + } + + @Test + void testInvite() { + assertGroup("-----------------------------------------------------" + + "\n[MVP+] 1wolvesgaming has invited you to join their party!" + + "\nYou have 60 seconds to accept. Click here to join!" + + "\n-----------------------------------------------------", + /* group: */ "invite", + /* expect: */ "1wolvesgaming"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java new file mode 100644 index 00000000..3a55ce5b --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java @@ -0,0 +1,22 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.config.SkyblockerConfig; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class DungeonChestProfitTest { + @Test + void testProfitText() { + SkyblockerConfig.DungeonChestProfit config = new SkyblockerConfig.DungeonChestProfit(); + Assertions.assertEquals("literal{ 0}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(0, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ 0}[style={color=blue}]", DungeonChestProfit.getProfitText(0, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(10, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10}[style={color=blue}]", DungeonChestProfit.getProfitText(10, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(-10, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10}[style={color=blue}]", DungeonChestProfit.getProfitText(-10, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10,000}[style={color=dark_green}]", DungeonChestProfit.getProfitText(10000, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ +10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(10000, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10,000}[style={color=red}]", DungeonChestProfit.getProfitText(-10000, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + Assertions.assertEquals("literal{ -10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(-10000, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java new file mode 100644 index 00000000..3772fd75 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java @@ -0,0 +1,19 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +class ThreeWeirdosTest extends ChatPatternListenerTest<ThreeWeirdos> { + public ThreeWeirdosTest() { + super(new ThreeWeirdos()); + } + + @Test + void test1() { + assertGroup("§e[NPC] §cBaxter§f: My chest doesn't have the reward. We are all telling the truth.", 1, "Baxter"); + } + @Test + void test2() { + assertGroup("§e[NPC] §cHope§f: The reward isn't in any of our chests.", 1, "Hope"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/TriviaTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/TriviaTest.java new file mode 100644 index 00000000..1df5a8e1 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/TriviaTest.java @@ -0,0 +1,33 @@ +package de.hysky.skyblocker.skyblock.dungeon; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +class TriviaTest extends ChatPatternListenerTest<Trivia> { + public TriviaTest() { + super(new Trivia()); + } + + @Test + void anyQuestion1() { + assertGroup(" What is the first question?", 1, "What is the first question?"); + } + + @Test + void anyQestion2() { + assertGroup(" How many questions are there?", 1, "How many questions are there?"); + } + + @Test + void answer1() { + assertGroup(" §6 ⓐ §aAnswer 1", 3, "Answer 1"); + } + @Test + void answer2() { + assertGroup(" §6 ⓑ §aAnswer 2", 3, "Answer 2"); + } + @Test + void answer3() { + assertGroup(" §6 ⓒ §aAnswer 3", 3, "Answer 3"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java new file mode 100644 index 00000000..3d2993cf --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java @@ -0,0 +1,167 @@ +package de.hysky.skyblocker.skyblock.dungeon.secrets; + +import net.minecraft.datafixer.fix.ItemIdFix; +import net.minecraft.datafixer.fix.ItemInstanceTheFlatteningFix; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.net.URL; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Utility class to convert the old dungeon rooms data from Dungeon Rooms Mod to a new format. + * The new format is similar to <a href="https://quantizr.github.io/posts/how-it-works/">DRM's format</a>, but uses ints instead of longs and a custom numeric block id to store the block states. + * The first byte is the x position, the second byte is the y position, the third byte is the z position, and the fourth byte is the custom numeric block id. + * Use {@link DungeonSecrets#NUMERIC_ID} to get the custom numeric block id of a block. + * Run this manually when updating dungeon rooms data with DRM's data in {@code src/test/resources/assets/skyblocker/dungeons/dungeonrooms}. + */ +public class DungeonRoomsDFU { + private static final Logger LOGGER = LoggerFactory.getLogger(DungeonRoomsDFU.class); + private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons"; + private static final String DUNGEON_ROOMS_DATA_DIR = DUNGEONS_DATA_DIR + "/dungeonrooms"; + private static final HashMap<String, HashMap<String, HashMap<String, long[]>>> OLD_ROOMS = new HashMap<>(); + private static final HashMap<String, HashMap<String, HashMap<String, int[]>>> ROOMS = new HashMap<>(); + + public static void main(String[] args) { + load().join(); + updateRooms(); + save().join(); + } + + private static CompletableFuture<Void> load() { + List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); + URL dungeonsURL = DungeonRoomsDFU.class.getResource(DUNGEON_ROOMS_DATA_DIR); + if (dungeonsURL == null) { + LOGGER.error("Failed to load dungeon secrets, unable to find dungeon rooms data directory"); + return CompletableFuture.completedFuture(null); + } + Path dungeonsDir = Path.of(dungeonsURL.getPath()); + int resourcePathIndex = dungeonsDir.toString().indexOf(DUNGEON_ROOMS_DATA_DIR); + try (DirectoryStream<Path> dungeons = Files.newDirectoryStream(dungeonsDir, Files::isDirectory)) { + for (Path dungeon : dungeons) { + try (DirectoryStream<Path> roomShapes = Files.newDirectoryStream(dungeon, Files::isDirectory)) { + List<CompletableFuture<Void>> roomShapeFutures = new ArrayList<>(); + HashMap<String, HashMap<String, long[]>> roomShapesMap = new HashMap<>(); + for (Path roomShape : roomShapes) { + roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> roomShapesMap.put(roomShape.getFileName().toString().toLowerCase(), rooms))); + } + OLD_ROOMS.put(dungeon.getFileName().toString().toLowerCase(), roomShapesMap); + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getFileName(), roomShapesMap.size(), roomShapesMap.values().stream().mapToInt(HashMap::size).sum()))); + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets for dungeon " + dungeon.getFileName(), e); + } + } + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets", e); + } + return CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", OLD_ROOMS.size(), OLD_ROOMS.values().stream().mapToInt(HashMap::size).sum(), OLD_ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); + } + + private static HashMap<String, long[]> readRooms(Path roomShape, int resourcePathIndex) { + try (DirectoryStream<Path> rooms = Files.newDirectoryStream(roomShape, Files::isRegularFile)) { + HashMap<String, long[]> roomsData = new HashMap<>(); + for (Path room : rooms) { + String name = room.getFileName().toString(); + //noinspection DataFlowIssue + try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(DungeonRoomsDFU.class.getResourceAsStream(room.toString().substring(resourcePathIndex))))) { + roomsData.put(name.substring(0, name.length() - 9).toLowerCase(), (long[]) in.readObject()); + LOGGER.info("Loaded dungeon secrets room {}", name); + } catch (NullPointerException | IOException | ClassNotFoundException e) { + LOGGER.error("Failed to load dungeon secrets room " + name, e); + } + } + LOGGER.info("Loaded dungeon secrets room shape {} with {} rooms", roomShape.getFileName(), roomsData.size()); + return roomsData; + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets room shape " + roomShape.getFileName(), e); + } + return null; + } + + private static void updateRooms() { + for (Map.Entry<String, HashMap<String, HashMap<String, long[]>>> oldDungeon : OLD_ROOMS.entrySet()) { + HashMap<String, HashMap<String, int[]>> dungeon = new HashMap<>(); + for (Map.Entry<String, HashMap<String, long[]>> oldRoomShape : oldDungeon.getValue().entrySet()) { + HashMap<String, int[]> roomShape = new HashMap<>(); + for (Map.Entry<String, long[]> oldRoomEntry : oldRoomShape.getValue().entrySet()) { + roomShape.put(oldRoomEntry.getKey().replaceAll(" ", "-"), updateRoom(oldRoomEntry.getValue())); + } + dungeon.put(oldRoomShape.getKey(), roomShape); + } + ROOMS.put(oldDungeon.getKey(), dungeon); + } + } + + private static int[] updateRoom(long[] oldRoom) { + int[] room = new int[oldRoom.length]; + for (int i = 0; i < oldRoom.length; i++) { + room[i] = updateBlock(oldRoom[i]); + } + // Technically not needed, as the long array should be sorted already. + Arrays.sort(room); + return room; + } + + /** + * Updates the block state from Dungeon Rooms Mod's format to the new format explained in {@link DungeonRoomsDFU}. + * + * @param oldBlock the old block state in DRM's format + * @return the new block state in the new format + */ + private static int updateBlock(long oldBlock) { + short x = (short) (oldBlock >> 48 & 0xFFFF); + short y = (short) (oldBlock >> 32 & 0xFFFF); + short z = (short) (oldBlock >> 16 & 0xFFFF); + // Blocks should be within the range 0 to 256, since a dungeon room is at most around 128 blocks long and around 150 blocks tall. + if (x < 0 || x > 0xFF || y < 0 || y > 0xFF || z < 0 || z > 0xFF) { + throw new IllegalArgumentException("Invalid block: " + oldBlock); + } + short oldId = (short) (oldBlock & 0xFFFF); + // Get the new id for the block. + String newId = ItemInstanceTheFlatteningFix.getItem(ItemIdFix.fromId(oldId / 100), oldId % 100); + if (newId == null) { + newId = ItemIdFix.fromId(oldId / 100); + } + return x << 24 | y << 16 | z << 8 | DungeonSecrets.NUMERIC_ID.getByte(newId); + } + + private static CompletableFuture<Void> save() { + List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); + for (Map.Entry<String, HashMap<String, HashMap<String, int[]>>> dungeon : ROOMS.entrySet()) { + Path dungeonDir = Path.of("out", "dungeons", dungeon.getKey()); + List<CompletableFuture<Void>> roomShapeFutures = new ArrayList<>(); + for (Map.Entry<String, HashMap<String, int[]>> roomShape : dungeon.getValue().entrySet()) { + Path roomShapeDir = dungeonDir.resolve(roomShape.getKey()); + roomShapeFutures.add(CompletableFuture.runAsync(() -> saveRooms(roomShapeDir, roomShape))); + } + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Saved dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getKey(), dungeon.getValue().size(), dungeon.getValue().values().stream().mapToInt(HashMap::size).sum()))); + } + return CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Saved dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); + } + + private static void saveRooms(Path roomShapeDir, Map.Entry<String, HashMap<String, int[]>> roomShape) { + try { + Files.createDirectories(roomShapeDir); + } catch (IOException e) { + LOGGER.error("Failed to save dungeon secrets: failed to create dungeon secrets room shape directory " + roomShapeDir, e); + } + for (Map.Entry<String, int[]> room : roomShape.getValue().entrySet()) { + try (ObjectOutputStream out = new ObjectOutputStream(new DeflaterOutputStream(Files.newOutputStream(roomShapeDir.resolve(room.getKey() + ".skeleton"))))) { + out.writeObject(room.getValue()); + LOGGER.info("Saved dungeon secrets room {}", room.getKey()); + } catch (IOException e) { + LOGGER.error("Failed to save dungeon secrets room " + room.getKey(), e); + } + } + LOGGER.info("Saved dungeon secrets room shape {} with {} rooms", roomShape.getKey(), roomShape.getValue().size()); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/RoomTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/RoomTest.java new file mode 100644 index 00000000..5309aae6 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/secrets/RoomTest.java @@ -0,0 +1,13 @@ +package de.hysky.skyblocker.skyblock.dungeon.secrets; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RoomTest { + @Test + void onChatMessage() { + Assertions.assertFalse(Room.isAllSecretsFound("§10,000/10,000❤ §a5,000§a❈ Defense §b2,000/2,000✎ Mana §70/1 Secrets")); + Assertions.assertTrue(Room.isAllSecretsFound("§1,000,000/10,000❤ §3+1,000.5 Combat (33.33%) §b4,000/2,000✎ Mana §710/10 Secrets")); + Assertions.assertTrue(Room.isAllSecretsFound("§1,000,000/10,000❤ §b-25 Mana (§6Instant Transmission§b) §b2,000/2,000✎ Mana §710/1 Secrets")); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dwarven/FetchurTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dwarven/FetchurTest.java new file mode 100644 index 00000000..08282972 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dwarven/FetchurTest.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +class FetchurTest extends ChatPatternListenerTest<Fetchur> { + public FetchurTest() { + super(new Fetchur()); + } + + @Test + public void patternCaptures() { + assertGroup("§e[NPC] Fetchur§f: its a hint", 1, "a hint"); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dwarven/PuzzlerTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dwarven/PuzzlerTest.java new file mode 100644 index 00000000..9437a5c9 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/dwarven/PuzzlerTest.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.dwarven; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +class PuzzlerTest extends ChatPatternListenerTest<Puzzler> { + public PuzzlerTest() { + super(new Puzzler()); + } + + @Test + void puzzler() { + assertGroup("§e[NPC] §dPuzzler§f: §b◀§d▲§b◀§d▲§d▲§5▶§5▶§b◀§d▲§a▼", 1, "§b◀§d▲§b◀§d▲§d▲§5▶§5▶§b◀§d▲§a▼"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/AbilityFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/AbilityFilterTest.java new file mode 100644 index 00000000..8e286d8f --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/AbilityFilterTest.java @@ -0,0 +1,19 @@ +package de.hysky.skyblocker.skyblock.filters; + +import org.junit.jupiter.api.Test; + +class AbilityFilterTest extends ChatFilterTest<AbilityFilter> { + public AbilityFilterTest() { + super(new AbilityFilter()); + } + + @Test + void charges() { + assertMatches("No more charges, next one in 13.2s!"); + } + + @Test + void cooldown() { + assertMatches("This ability is on cooldown for 42s."); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/AdFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/AdFilterTest.java new file mode 100644 index 00000000..3eec1cd9 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/AdFilterTest.java @@ -0,0 +1,68 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +import java.util.regex.Matcher; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AdFilterTest extends ChatPatternListenerTest<AdFilter> { + public AdFilterTest() { + super(new AdFilter()); + } + + @Test + void noRank() { + assertMatches("§8[§a86§8] §7Advertiser§7: advertisement"); + } + + @Test + void vip() { + assertMatches("§8[§b280§8] §a[VIP] Advertiser§f: advertisement"); + } + + @Test + void mvp() { + assertMatches("§8[§d256§8] §6§l⚡ §b[MVP§c+§b] Advertiser§f: advertisement"); + } + + @Test + void plusPlus() { + assertMatches("§8[§6222§8] §6[MVP§c++§6] Advertiser§f: advertisement"); + } + + @Test + void capturesMessage() { + assertGroup("§8[§c325§8] §b[MVP§c+§b] b2dderr§f: buying prismapump", 2, "buying prismapump"); + } + + @Test + void simpleAd() { + assertFilters("§8[§e320§8] §b[MVP§c+§b] b2dderr§f: buying prismapump"); + } + + @Test + void uppercaseAd() { + assertFilters("§8[§f70§8] §a[VIP] Tecnoisnoob§f: SELLING REJUVENATE 5 Book on ah!"); + } + + @Test + void characterSpam() { + assertFilters("§8[§9144§8] §a[VIP] Benyyy_§f: Hey, Visit my Island, i spent lots of time to build it! I also made donate room! <<<<<<<<<<<<<<<<<<<"); + } + + @Test + void notAd() { + Matcher matcher = listener.pattern.matcher("§8[§6200§8] §a[VIP] NotMatching§f: This message shouldn't match!"); + assertTrue(matcher.matches()); + assertFalse(listener.onMatch(null, matcher)); + } + + void assertFilters(String message) { + Matcher matcher = listener.pattern.matcher(message); + assertTrue(matcher.matches()); + assertTrue(listener.onMatch(null, matcher)); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/AoteFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/AoteFilterTest.java new file mode 100644 index 00000000..3115a48d --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/AoteFilterTest.java @@ -0,0 +1,14 @@ +package de.hysky.skyblocker.skyblock.filters; + +import org.junit.jupiter.api.Test; + +class AoteFilterTest extends ChatFilterTest<AoteFilter> { + public AoteFilterTest() { + super(new AoteFilter()); + } + + @Test + void testRegex() { + assertMatches("There are blocks in the way!"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/AutopetFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/AutopetFilterTest.java new file mode 100644 index 00000000..846acbb8 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/AutopetFilterTest.java @@ -0,0 +1,15 @@ +package de.hysky.skyblocker.skyblock.filters; + +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; +import org.junit.jupiter.api.Test; + +class AutopetFilterTest extends ChatPatternListenerTest<AutopetFilter> { + public AutopetFilterTest() { + super(new AutopetFilter()); + } + + @Test + void testAutopet() { + assertMatches("§cAutopet §eequipped your §7[Lvl 85] §6Tiger§e! §a§lVIEW RULE"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java new file mode 100644 index 00000000..7b779945 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java @@ -0,0 +1,10 @@ +package de.hysky.skyblocker.skyblock.filters; + +import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; +import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; + +public class ChatFilterTest<T extends ChatPatternListener> extends ChatPatternListenerTest<T> { + public ChatFilterTest(T listener) { + super(listener); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/ComboFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/ComboFilterTest.java new file mode 100644 index 00000000..85b01b4b --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/ComboFilterTest.java @@ -0,0 +1,29 @@ +package de.hysky.skyblocker.skyblock.filters; + +import org.junit.jupiter.api.Test; + +public class ComboFilterTest extends ChatFilterTest<ComboFilter> { + public ComboFilterTest() { + super(new ComboFilter()); + } + + @Test + void testComboMF() { + assertMatches("+5 Kill Combo +3% ✯ Magic Find"); + } + + @Test + void testComboCoins() { + assertMatches("+10 Kill Combo +10 coins per kill"); + } + + @Test + void testComboEXP() { + assertMatches("+20 Kill Combo +15% Combat Exp"); + } + + @Test + void testComboExpired() { + assertMatches("Your Kill Combo has expired! You reached a 11 Kill Combo!"); + } +} diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/HealFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/HealFilterTest.java new file mode 100644 index 00000000..d57bf0dc --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/HealFilterTest.java @@ -0,0 +1,19 @@ +package de.hysky.skyblocker.skyblock.filters; + +import org.junit.jupiter.api.Test; + +class HealFilterTest extends ChatFilterTest<HealFilter> { + public HealFilterTest() { + super(new HealFilter()); + } + + @Test + void healSelf() { + assertMatches("You healed yourself for 18.3 health!"); + } + + @Test + void healedYou() { + assertMatches("H3aler_ healed you for 56 health!"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilterTest.java new file mode 100644 index 00000000..327fcebb --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilterTest.java @@ -0,0 +1,19 @@ +package de.hysky.skyblocker.skyblock.filters; + +import org.junit.jupiter.api.Test; + +class ImplosionFilterTest extends ChatFilterTest<ImplosionFilter> { + public ImplosionFilterTest() { + super(new ImplosionFilter()); + } + + @Test + void oneEnemy() { + assertMatches("Your Implosion hit 1 enemy for 636,116.8 damage."); + } + + @Test + void multipleEnemies() { + assertMatches("Your Implosion hit 7 enemies for 4,452,817.4 damage."); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilterTest.java new file mode 100644 index 00000000..681d64c0 --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/TeleportPadFilterTest.java @@ -0,0 +1,19 @@ +package de.hysky.skyblocker.skyblock.filters; + +import org.junit.jupiter.api.Test; + +public class TeleportPadFilterTest extends ChatFilterTest<TeleportPadFilter> { + public TeleportPadFilterTest() { + super(new TeleportPadFilter()); + } + + @Test + void testTeleport() { + assertMatches("Warped from the Base Teleport Pad to the Minion Teleport Pad!"); + } + + @Test + void testNoDestination() { + assertMatches("This Teleport Pad does not have a destination set!"); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java b/src/test/java/de/hysky/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java new file mode 100644 index 00000000..36b65cae --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java @@ -0,0 +1,27 @@ +package de.hysky.skyblocker.skyblock.item; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import net.minecraft.util.Identifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ArmorTrimIdSerializationTest { + private final Gson gson = new GsonBuilder().registerTypeAdapter(Identifier.class, new Identifier.Serializer()).create(); + + @Test + void serialize() { + CustomArmorTrims.ArmorTrimId armorTrimId = new CustomArmorTrims.ArmorTrimId(new Identifier("material_id"), new Identifier("pattern_id")); + String json = gson.toJson(armorTrimId); + String expectedJson = "{\"material\":\"minecraft:material_id\",\"pattern\":\"minecraft:pattern_id\"}"; + Assertions.assertEquals(expectedJson, json); + } + + @Test + void deserialize() { + String json = "{\"material\":\"minecraft:material_id\",\"pattern\":\"minecraft:pattern_id\"}"; + CustomArmorTrims.ArmorTrimId armorTrimId = gson.fromJson(json, CustomArmorTrims.ArmorTrimId.class); + CustomArmorTrims.ArmorTrimId expectedArmorTrimId = new CustomArmorTrims.ArmorTrimId(new Identifier("material_id"), new Identifier("pattern_id")); + Assertions.assertEquals(expectedArmorTrimId, armorTrimId); + } +} diff --git a/src/test/java/de/hysky/skyblocker/utils/chat/ChatPatternListenerTest.java b/src/test/java/de/hysky/skyblocker/utils/chat/ChatPatternListenerTest.java new file mode 100644 index 00000000..8b670cbb --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/utils/chat/ChatPatternListenerTest.java @@ -0,0 +1,28 @@ +package de.hysky.skyblocker.utils.chat; + +import java.util.regex.Matcher; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public abstract class ChatPatternListenerTest<T extends ChatPatternListener> { + protected final T listener; + + public ChatPatternListenerTest(T listener) { + this.listener = listener; + } + + protected Matcher matcher(String message) { + return listener.pattern.matcher(message); + } + + protected void assertMatches(String message) { + assertTrue(matcher(message).matches()); + } + + protected void assertGroup(String message, int group, String expect) { + Matcher matcher = matcher(message); + assertTrue(matcher.matches()); + assertEquals(expect, matcher.group(group)); + } +} \ No newline at end of file diff --git a/src/test/java/de/hysky/skyblocker/utils/scheduler/SchedulerTest.java b/src/test/java/de/hysky/skyblocker/utils/scheduler/SchedulerTest.java new file mode 100644 index 00000000..429273ac --- /dev/null +++ b/src/test/java/de/hysky/skyblocker/utils/scheduler/SchedulerTest.java @@ -0,0 +1,88 @@ +package de.hysky.skyblocker.utils.scheduler; + +import org.apache.commons.lang3.mutable.MutableInt; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class SchedulerTest { + private final MutableInt currentTick = new MutableInt(0); + private final MutableInt cycleCount1 = new MutableInt(0); + private final MutableInt cycleCount2 = new MutableInt(0); + private final MutableInt cycleCount3 = new MutableInt(0); + private final MutableInt cycleCount4 = new MutableInt(0); + private final MutableInt cycleCount5 = new MutableInt(0); + private final MutableInt cycleCount6 = new MutableInt(0); + private final MutableInt cycleCount7 = new MutableInt(0); + private final MutableInt cycleCount8 = new MutableInt(0); + + @Test + public void testSchedule() { + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(0, currentTick.intValue()), 0); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(1, currentTick.intValue()), 1); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(2, currentTick.intValue()), 2); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(10, currentTick.intValue()), 10); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(20, currentTick.intValue()), 20); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(50, currentTick.intValue()), 50); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(100, currentTick.intValue()), 100); + Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(123, currentTick.intValue()), 123); + Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); + Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); + Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); + Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); + Scheduler.INSTANCE.scheduleCyclic(() -> { + Assertions.assertEquals(cycleCount1.intValue(), currentTick.intValue()); + cycleCount1.increment(); + }, 1); + Scheduler.INSTANCE.scheduleCyclic(() -> { + Assertions.assertEquals(0, currentTick.intValue() % 10); + Assertions.assertEquals(cycleCount2.intValue(), currentTick.intValue() / 10); + cycleCount2.increment(); + }, 10); + Scheduler.INSTANCE.scheduleCyclic(() -> { + Assertions.assertEquals(0, currentTick.intValue() % 55); + Assertions.assertEquals(cycleCount3.intValue(), currentTick.intValue() / 55); + cycleCount3.increment(); + }, 55); + Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(() -> { + Assertions.assertEquals(7, currentTick.intValue() % 10); + Assertions.assertEquals(cycleCount4.intValue(), currentTick.intValue() / 10); + cycleCount4.increment(); + }, 10), 7); + Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(() -> { + Assertions.assertEquals(0, currentTick.intValue() % 75); + Assertions.assertEquals(cycleCount5.intValue(), currentTick.intValue() / 75); + cycleCount5.increment(); + }, 75), 0); + Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(() -> { + Assertions.assertEquals(1, currentTick.intValue() % 99); + Assertions.assertEquals(cycleCount6.intValue(), currentTick.intValue() / 99); + cycleCount6.increment(); + }, 99), 1); + Scheduler.INSTANCE.scheduleCyclic(() -> Scheduler.INSTANCE.schedule(() -> { + Assertions.assertEquals(5, currentTick.intValue() % 10); + Assertions.assertEquals(cycleCount7.intValue(), currentTick.intValue() / 10); + cycleCount7.increment(); + }, 5), 10); + Scheduler.INSTANCE.scheduleCyclic(() -> Scheduler.INSTANCE.schedule(() -> { + Assertions.assertEquals(10, currentTick.intValue() % 55); + Assertions.assertEquals(cycleCount8.intValue(), currentTick.intValue() / 55); + cycleCount8.increment(); + }, 10), 55); + while (currentTick.intValue() < 100_000) { + tick(); + } + Assertions.assertEquals(100000, cycleCount1.intValue()); + Assertions.assertEquals(10000, cycleCount2.intValue()); + Assertions.assertEquals(1819, cycleCount3.intValue()); + Assertions.assertEquals(10000, cycleCount4.intValue()); + Assertions.assertEquals(1334, cycleCount5.intValue()); + Assertions.assertEquals(1011, cycleCount6.intValue()); + Assertions.assertEquals(10000, cycleCount7.intValue()); + Assertions.assertEquals(1818, cycleCount8.intValue()); + } + + private void tick() { + Scheduler.INSTANCE.tick(); + currentTick.increment(); + } +} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTrackerTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTrackerTest.java deleted file mode 100644 index da919699..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/StatusBarTrackerTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class StatusBarTrackerTest { - private StatusBarTracker tracker; - - @BeforeEach - void setUp() { - tracker = new StatusBarTracker(); - } - - void assertStats(int hp, int maxHp, int def, int mana, int maxMana, int overflowMana) { - int absorption = 0; - if(hp > maxHp) { - absorption = hp - maxHp; - hp -= absorption; - if(absorption > maxHp) - absorption = maxHp; - } - assertEquals(new StatusBarTracker.Resource(hp, maxHp, absorption), tracker.getHealth()); - assertEquals(new StatusBarTracker.Resource(mana, maxMana, overflowMana), tracker.getMana()); - } - - @Test - void normalStatusBar() { - String res = tracker.update("§c934/1086❤ §a159§a❈ Defense §b562/516✎ Mana", false); - assertNull(res); - assertStats(934, 1086, 159, 562, 516, 0); - } - - @Test - void overflowMana() { - String res = tracker.update("§61605/1305❤ §a270§a❈ Defense §b548/548✎ §3200ʬ", false); - assertNull(res); - assertStats(1605, 1305, 270, 548, 548, 200); - } - - @Test - void regeneration() { - String res = tracker.update("§c2484/2484❤+§c120▄ §a642§a❈ Defense §b2557/2611✎ Mana", false); - assertEquals("§c❤+§c120▄", res); - } - - @Test - void instantTransmission() { - String actionBar = "§c2259/2259❤ §b-20 Mana (§6Instant Transmission§b) §b549/2676✎ Mana"; - assertEquals("§b-20 Mana (§6Instant Transmission§b)", tracker.update(actionBar, false)); - assertNull(tracker.update(actionBar, true)); - } - - @Test - void rapidFire() { - String actionBar = "§c2509/2509❤ §b-48 Mana (§6Rapid-fire§b) §b2739/2811✎ Mana"; - assertEquals("§b-48 Mana (§6Rapid-fire§b)", tracker.update(actionBar, false)); - assertNull(tracker.update(actionBar, true)); - } - - @Test - void zombieSword() { - String actionBar = "§c2509/2509❤ §b-56 Mana (§6Instant Heal§b) §b2674/2821✎ Mana §e§lⓩⓩⓩⓩ§6§lⓄ"; - assertEquals("§b-56 Mana (§6Instant Heal§b) §e§lⓩⓩⓩⓩ§6§lⓄ", tracker.update(actionBar, false)); - assertEquals("§e§lⓩⓩⓩⓩ§6§lⓄ", tracker.update(actionBar, true)); - } - - @Test - void campfire() { - String res = tracker.update("§c17070/25565❤+§c170▃ §65,625 DPS §c1 second §b590/626✎ §3106ʬ", false); - assertEquals("§c❤+§c170▃ §65,625 DPS §c1 second", res); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java deleted file mode 100644 index eb867a37..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/AcceptRepartyTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -import java.util.regex.Matcher; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class AcceptRepartyTest extends ChatPatternListenerTest<Reparty> { - - public AcceptRepartyTest() { super(new Reparty()); } - - protected void assertGroup(String message, String group, String expect) { - Matcher matcher = matcher(message); - assertTrue(matcher.matches()); - assertEquals(expect, matcher.group(group)); - } - - @Test - void testDisband() { - assertGroup("[VIP+] KoloiYolo has disbanded the party!", - /* group: */ "disband", - /* expect: */ "KoloiYolo"); - } - - @Test - void testInvite() { - assertGroup("-----------------------------------------------------" + - "\n[MVP+] 1wolvesgaming has invited you to join their party!" + - "\nYou have 60 seconds to accept. Click here to join!" + - "\n-----------------------------------------------------", - /* group: */ "invite", - /* expect: */ "1wolvesgaming"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java deleted file mode 100644 index 9ff1e154..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfitTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.config.SkyblockerConfig; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class DungeonChestProfitTest { - @Test - void testProfitText() { - SkyblockerConfig.DungeonChestProfit config = new SkyblockerConfig.DungeonChestProfit(); - Assertions.assertEquals("literal{ 0}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(0, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ 0}[style={color=blue}]", DungeonChestProfit.getProfitText(0, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ +10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(10, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ +10}[style={color=blue}]", DungeonChestProfit.getProfitText(10, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ -10}[style={color=dark_gray}]", DungeonChestProfit.getProfitText(-10, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ -10}[style={color=blue}]", DungeonChestProfit.getProfitText(-10, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ +10,000}[style={color=dark_green}]", DungeonChestProfit.getProfitText(10000, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ +10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(10000, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ -10,000}[style={color=red}]", DungeonChestProfit.getProfitText(-10000, false, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - Assertions.assertEquals("literal{ -10,000}[style={color=blue}]", DungeonChestProfit.getProfitText(-10000, true, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor).toString()); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java deleted file mode 100644 index 6a5dd2a7..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdosTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -class ThreeWeirdosTest extends ChatPatternListenerTest<ThreeWeirdos> { - public ThreeWeirdosTest() { - super(new ThreeWeirdos()); - } - - @Test - void test1() { - assertGroup("§e[NPC] §cBaxter§f: My chest doesn't have the reward. We are all telling the truth.", 1, "Baxter"); - } - @Test - void test2() { - assertGroup("§e[NPC] §cHope§f: The reward isn't in any of our chests.", 1, "Hope"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TriviaTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TriviaTest.java deleted file mode 100644 index a787967d..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TriviaTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -class TriviaTest extends ChatPatternListenerTest<Trivia> { - public TriviaTest() { - super(new Trivia()); - } - - @Test - void anyQuestion1() { - assertGroup(" What is the first question?", 1, "What is the first question?"); - } - - @Test - void anyQestion2() { - assertGroup(" How many questions are there?", 1, "How many questions are there?"); - } - - @Test - void answer1() { - assertGroup(" §6 ⓐ §aAnswer 1", 3, "Answer 1"); - } - @Test - void answer2() { - assertGroup(" §6 ⓑ §aAnswer 2", 3, "Answer 2"); - } - @Test - void answer3() { - assertGroup(" §6 ⓒ §aAnswer 3", 3, "Answer 3"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java deleted file mode 100644 index 5e4cdeef..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java +++ /dev/null @@ -1,167 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; - -import net.minecraft.datafixer.fix.ItemIdFix; -import net.minecraft.datafixer.fix.ItemInstanceTheFlatteningFix; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.net.URL; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.zip.DeflaterOutputStream; -import java.util.zip.InflaterInputStream; - -/** - * Utility class to convert the old dungeon rooms data from Dungeon Rooms Mod to a new format. - * The new format is similar to <a href="https://quantizr.github.io/posts/how-it-works/">DRM's format</a>, but uses ints instead of longs and a custom numeric block id to store the block states. - * The first byte is the x position, the second byte is the y position, the third byte is the z position, and the fourth byte is the custom numeric block id. - * Use {@link DungeonSecrets#NUMERIC_ID} to get the custom numeric block id of a block. - * Run this manually when updating dungeon rooms data with DRM's data in {@code src/test/resources/assets/skyblocker/dungeons/dungeonrooms}. - */ -public class DungeonRoomsDFU { - private static final Logger LOGGER = LoggerFactory.getLogger(DungeonRoomsDFU.class); - private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons"; - private static final String DUNGEON_ROOMS_DATA_DIR = DUNGEONS_DATA_DIR + "/dungeonrooms"; - private static final HashMap<String, HashMap<String, HashMap<String, long[]>>> OLD_ROOMS = new HashMap<>(); - private static final HashMap<String, HashMap<String, HashMap<String, int[]>>> ROOMS = new HashMap<>(); - - public static void main(String[] args) { - load().join(); - updateRooms(); - save().join(); - } - - private static CompletableFuture<Void> load() { - List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); - URL dungeonsURL = DungeonRoomsDFU.class.getResource(DUNGEON_ROOMS_DATA_DIR); - if (dungeonsURL == null) { - LOGGER.error("Failed to load dungeon secrets, unable to find dungeon rooms data directory"); - return CompletableFuture.completedFuture(null); - } - Path dungeonsDir = Path.of(dungeonsURL.getPath()); - int resourcePathIndex = dungeonsDir.toString().indexOf(DUNGEON_ROOMS_DATA_DIR); - try (DirectoryStream<Path> dungeons = Files.newDirectoryStream(dungeonsDir, Files::isDirectory)) { - for (Path dungeon : dungeons) { - try (DirectoryStream<Path> roomShapes = Files.newDirectoryStream(dungeon, Files::isDirectory)) { - List<CompletableFuture<Void>> roomShapeFutures = new ArrayList<>(); - HashMap<String, HashMap<String, long[]>> roomShapesMap = new HashMap<>(); - for (Path roomShape : roomShapes) { - roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> roomShapesMap.put(roomShape.getFileName().toString().toLowerCase(), rooms))); - } - OLD_ROOMS.put(dungeon.getFileName().toString().toLowerCase(), roomShapesMap); - dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getFileName(), roomShapesMap.size(), roomShapesMap.values().stream().mapToInt(HashMap::size).sum()))); - } catch (IOException e) { - LOGGER.error("Failed to load dungeon secrets for dungeon " + dungeon.getFileName(), e); - } - } - } catch (IOException e) { - LOGGER.error("Failed to load dungeon secrets", e); - } - return CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", OLD_ROOMS.size(), OLD_ROOMS.values().stream().mapToInt(HashMap::size).sum(), OLD_ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); - } - - private static HashMap<String, long[]> readRooms(Path roomShape, int resourcePathIndex) { - try (DirectoryStream<Path> rooms = Files.newDirectoryStream(roomShape, Files::isRegularFile)) { - HashMap<String, long[]> roomsData = new HashMap<>(); - for (Path room : rooms) { - String name = room.getFileName().toString(); - //noinspection DataFlowIssue - try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(DungeonRoomsDFU.class.getResourceAsStream(room.toString().substring(resourcePathIndex))))) { - roomsData.put(name.substring(0, name.length() - 9).toLowerCase(), (long[]) in.readObject()); - LOGGER.info("Loaded dungeon secrets room {}", name); - } catch (NullPointerException | IOException | ClassNotFoundException e) { - LOGGER.error("Failed to load dungeon secrets room " + name, e); - } - } - LOGGER.info("Loaded dungeon secrets room shape {} with {} rooms", roomShape.getFileName(), roomsData.size()); - return roomsData; - } catch (IOException e) { - LOGGER.error("Failed to load dungeon secrets room shape " + roomShape.getFileName(), e); - } - return null; - } - - private static void updateRooms() { - for (Map.Entry<String, HashMap<String, HashMap<String, long[]>>> oldDungeon : OLD_ROOMS.entrySet()) { - HashMap<String, HashMap<String, int[]>> dungeon = new HashMap<>(); - for (Map.Entry<String, HashMap<String, long[]>> oldRoomShape : oldDungeon.getValue().entrySet()) { - HashMap<String, int[]> roomShape = new HashMap<>(); - for (Map.Entry<String, long[]> oldRoomEntry : oldRoomShape.getValue().entrySet()) { - roomShape.put(oldRoomEntry.getKey().replaceAll(" ", "-"), updateRoom(oldRoomEntry.getValue())); - } - dungeon.put(oldRoomShape.getKey(), roomShape); - } - ROOMS.put(oldDungeon.getKey(), dungeon); - } - } - - private static int[] updateRoom(long[] oldRoom) { - int[] room = new int[oldRoom.length]; - for (int i = 0; i < oldRoom.length; i++) { - room[i] = updateBlock(oldRoom[i]); - } - // Technically not needed, as the long array should be sorted already. - Arrays.sort(room); - return room; - } - - /** - * Updates the block state from Dungeon Rooms Mod's format to the new format explained in {@link DungeonRoomsDFU}. - * - * @param oldBlock the old block state in DRM's format - * @return the new block state in the new format - */ - private static int updateBlock(long oldBlock) { - short x = (short) (oldBlock >> 48 & 0xFFFF); - short y = (short) (oldBlock >> 32 & 0xFFFF); - short z = (short) (oldBlock >> 16 & 0xFFFF); - // Blocks should be within the range 0 to 256, since a dungeon room is at most around 128 blocks long and around 150 blocks tall. - if (x < 0 || x > 0xFF || y < 0 || y > 0xFF || z < 0 || z > 0xFF) { - throw new IllegalArgumentException("Invalid block: " + oldBlock); - } - short oldId = (short) (oldBlock & 0xFFFF); - // Get the new id for the block. - String newId = ItemInstanceTheFlatteningFix.getItem(ItemIdFix.fromId(oldId / 100), oldId % 100); - if (newId == null) { - newId = ItemIdFix.fromId(oldId / 100); - } - return x << 24 | y << 16 | z << 8 | DungeonSecrets.NUMERIC_ID.getByte(newId); - } - - private static CompletableFuture<Void> save() { - List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); - for (Map.Entry<String, HashMap<String, HashMap<String, int[]>>> dungeon : ROOMS.entrySet()) { - Path dungeonDir = Path.of("out", "dungeons", dungeon.getKey()); - List<CompletableFuture<Void>> roomShapeFutures = new ArrayList<>(); - for (Map.Entry<String, HashMap<String, int[]>> roomShape : dungeon.getValue().entrySet()) { - Path roomShapeDir = dungeonDir.resolve(roomShape.getKey()); - roomShapeFutures.add(CompletableFuture.runAsync(() -> saveRooms(roomShapeDir, roomShape))); - } - dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Saved dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getKey(), dungeon.getValue().size(), dungeon.getValue().values().stream().mapToInt(HashMap::size).sum()))); - } - return CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Saved dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); - } - - private static void saveRooms(Path roomShapeDir, Map.Entry<String, HashMap<String, int[]>> roomShape) { - try { - Files.createDirectories(roomShapeDir); - } catch (IOException e) { - LOGGER.error("Failed to save dungeon secrets: failed to create dungeon secrets room shape directory " + roomShapeDir, e); - } - for (Map.Entry<String, int[]> room : roomShape.getValue().entrySet()) { - try (ObjectOutputStream out = new ObjectOutputStream(new DeflaterOutputStream(Files.newOutputStream(roomShapeDir.resolve(room.getKey() + ".skeleton"))))) { - out.writeObject(room.getValue()); - LOGGER.info("Saved dungeon secrets room {}", room.getKey()); - } catch (IOException e) { - LOGGER.error("Failed to save dungeon secrets room " + room.getKey(), e); - } - } - LOGGER.info("Saved dungeon secrets room shape {} with {} rooms", roomShape.getKey(), roomShape.getValue().size()); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java deleted file mode 100644 index b704037c..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class RoomTest { - @Test - void onChatMessage() { - Assertions.assertFalse(Room.isAllSecretsFound("§10,000/10,000❤ §a5,000§a❈ Defense §b2,000/2,000✎ Mana §70/1 Secrets")); - Assertions.assertTrue(Room.isAllSecretsFound("§1,000,000/10,000❤ §3+1,000.5 Combat (33.33%) §b4,000/2,000✎ Mana §710/10 Secrets")); - Assertions.assertTrue(Room.isAllSecretsFound("§1,000,000/10,000❤ §b-25 Mana (§6Instant Transmission§b) §b2,000/2,000✎ Mana §710/1 Secrets")); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/FetchurTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/FetchurTest.java deleted file mode 100644 index 5d78beeb..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/FetchurTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dwarven; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -class FetchurTest extends ChatPatternListenerTest<Fetchur> { - public FetchurTest() { - super(new Fetchur()); - } - - @Test - public void patternCaptures() { - assertGroup("§e[NPC] Fetchur§f: its a hint", 1, "a hint"); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/PuzzlerTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/PuzzlerTest.java deleted file mode 100644 index 8b92e9f5..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dwarven/PuzzlerTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.dwarven; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -class PuzzlerTest extends ChatPatternListenerTest<Puzzler> { - public PuzzlerTest() { - super(new Puzzler()); - } - - @Test - void puzzler() { - assertGroup("§e[NPC] §dPuzzler§f: §b◀§d▲§b◀§d▲§d▲§5▶§5▶§b◀§d▲§a▼", 1, "§b◀§d▲§b◀§d▲§d▲§5▶§5▶§b◀§d▲§a▼"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilterTest.java deleted file mode 100644 index de82039e..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AbilityFilterTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import org.junit.jupiter.api.Test; - -class AbilityFilterTest extends ChatFilterTest<AbilityFilter> { - public AbilityFilterTest() { - super(new AbilityFilter()); - } - - @Test - void charges() { - assertMatches("No more charges, next one in 13.2s!"); - } - - @Test - void cooldown() { - assertMatches("This ability is on cooldown for 42s."); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilterTest.java deleted file mode 100644 index 30315131..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AdFilterTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -import java.util.regex.Matcher; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -class AdFilterTest extends ChatPatternListenerTest<AdFilter> { - public AdFilterTest() { - super(new AdFilter()); - } - - @Test - void noRank() { - assertMatches("§8[§a86§8] §7Advertiser§7: advertisement"); - } - - @Test - void vip() { - assertMatches("§8[§b280§8] §a[VIP] Advertiser§f: advertisement"); - } - - @Test - void mvp() { - assertMatches("§8[§d256§8] §6§l⚡ §b[MVP§c+§b] Advertiser§f: advertisement"); - } - - @Test - void plusPlus() { - assertMatches("§8[§6222§8] §6[MVP§c++§6] Advertiser§f: advertisement"); - } - - @Test - void capturesMessage() { - assertGroup("§8[§c325§8] §b[MVP§c+§b] b2dderr§f: buying prismapump", 2, "buying prismapump"); - } - - @Test - void simpleAd() { - assertFilters("§8[§e320§8] §b[MVP§c+§b] b2dderr§f: buying prismapump"); - } - - @Test - void uppercaseAd() { - assertFilters("§8[§f70§8] §a[VIP] Tecnoisnoob§f: SELLING REJUVENATE 5 Book on ah!"); - } - - @Test - void characterSpam() { - assertFilters("§8[§9144§8] §a[VIP] Benyyy_§f: Hey, Visit my Island, i spent lots of time to build it! I also made donate room! <<<<<<<<<<<<<<<<<<<"); - } - - @Test - void notAd() { - Matcher matcher = listener.pattern.matcher("§8[§6200§8] §a[VIP] NotMatching§f: This message shouldn't match!"); - assertTrue(matcher.matches()); - assertFalse(listener.onMatch(null, matcher)); - } - - void assertFilters(String message) { - Matcher matcher = listener.pattern.matcher(message); - assertTrue(matcher.matches()); - assertTrue(listener.onMatch(null, matcher)); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilterTest.java deleted file mode 100644 index 52903b1b..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AoteFilterTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import org.junit.jupiter.api.Test; - -class AoteFilterTest extends ChatFilterTest<AoteFilter> { - public AoteFilterTest() { - super(new AoteFilter()); - } - - @Test - void testRegex() { - assertMatches("There are blocks in the way!"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilterTest.java deleted file mode 100644 index 2c8f1425..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/AutopetFilterTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; -import org.junit.jupiter.api.Test; - -class AutopetFilterTest extends ChatPatternListenerTest<AutopetFilter> { - public AutopetFilterTest() { - super(new AutopetFilter()); - } - - @Test - void testAutopet() { - assertMatches("§cAutopet §eequipped your §7[Lvl 85] §6Tiger§e! §a§lVIEW RULE"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ChatFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ChatFilterTest.java deleted file mode 100644 index 5d397d16..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ChatFilterTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; - -public class ChatFilterTest<T extends ChatPatternListener> extends ChatPatternListenerTest<T> { - public ChatFilterTest(T listener) { - super(listener); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilterTest.java deleted file mode 100644 index 2914db39..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ComboFilterTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import org.junit.jupiter.api.Test; - -public class ComboFilterTest extends ChatFilterTest<ComboFilter> { - public ComboFilterTest() { - super(new ComboFilter()); - } - - @Test - void testComboMF() { - assertMatches("+5 Kill Combo +3% ✯ Magic Find"); - } - - @Test - void testComboCoins() { - assertMatches("+10 Kill Combo +10 coins per kill"); - } - - @Test - void testComboEXP() { - assertMatches("+20 Kill Combo +15% Combat Exp"); - } - - @Test - void testComboExpired() { - assertMatches("Your Kill Combo has expired! You reached a 11 Kill Combo!"); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilterTest.java deleted file mode 100644 index d720d4be..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/HealFilterTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import org.junit.jupiter.api.Test; - -class HealFilterTest extends ChatFilterTest<HealFilter> { - public HealFilterTest() { - super(new HealFilter()); - } - - @Test - void healSelf() { - assertMatches("You healed yourself for 18.3 health!"); - } - - @Test - void healedYou() { - assertMatches("H3aler_ healed you for 56 health!"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilterTest.java deleted file mode 100644 index b9b872d9..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/ImplosionFilterTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import org.junit.jupiter.api.Test; - -class ImplosionFilterTest extends ChatFilterTest<ImplosionFilter> { - public ImplosionFilterTest() { - super(new ImplosionFilter()); - } - - @Test - void oneEnemy() { - assertMatches("Your Implosion hit 1 enemy for 636,116.8 damage."); - } - - @Test - void multipleEnemies() { - assertMatches("Your Implosion hit 7 enemies for 4,452,817.4 damage."); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilterTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilterTest.java deleted file mode 100644 index b8a96703..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/filters/TeleportPadFilterTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.filters; - -import org.junit.jupiter.api.Test; - -public class TeleportPadFilterTest extends ChatFilterTest<TeleportPadFilter> { - public TeleportPadFilterTest() { - super(new TeleportPadFilter()); - } - - @Test - void testTeleport() { - assertMatches("Warped from the Base Teleport Pad to the Minion Teleport Pad!"); - } - - @Test - void testNoDestination() { - assertMatches("This Teleport Pad does not have a destination set!"); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java deleted file mode 100644 index a6ef79b0..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/item/ArmorTrimIdSerializationTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package me.xmrvizzy.skyblocker.skyblock.item; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import net.minecraft.util.Identifier; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class ArmorTrimIdSerializationTest { - private final Gson gson = new GsonBuilder().registerTypeAdapter(Identifier.class, new Identifier.Serializer()).create(); - - @Test - void serialize() { - CustomArmorTrims.ArmorTrimId armorTrimId = new CustomArmorTrims.ArmorTrimId(new Identifier("material_id"), new Identifier("pattern_id")); - String json = gson.toJson(armorTrimId); - String expectedJson = "{\"material\":\"minecraft:material_id\",\"pattern\":\"minecraft:pattern_id\"}"; - Assertions.assertEquals(expectedJson, json); - } - - @Test - void deserialize() { - String json = "{\"material\":\"minecraft:material_id\",\"pattern\":\"minecraft:pattern_id\"}"; - CustomArmorTrims.ArmorTrimId armorTrimId = gson.fromJson(json, CustomArmorTrims.ArmorTrimId.class); - CustomArmorTrims.ArmorTrimId expectedArmorTrimId = new CustomArmorTrims.ArmorTrimId(new Identifier("material_id"), new Identifier("pattern_id")); - Assertions.assertEquals(expectedArmorTrimId, armorTrimId); - } -} diff --git a/src/test/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListenerTest.java b/src/test/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListenerTest.java deleted file mode 100644 index 5d6f5727..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/utils/chat/ChatPatternListenerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.chat; - -import java.util.regex.Matcher; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public abstract class ChatPatternListenerTest<T extends ChatPatternListener> { - protected final T listener; - - public ChatPatternListenerTest(T listener) { - this.listener = listener; - } - - protected Matcher matcher(String message) { - return listener.pattern.matcher(message); - } - - protected void assertMatches(String message) { - assertTrue(matcher(message).matches()); - } - - protected void assertGroup(String message, int group, String expect) { - Matcher matcher = matcher(message); - assertTrue(matcher.matches()); - assertEquals(expect, matcher.group(group)); - } -} \ No newline at end of file diff --git a/src/test/java/me/xmrvizzy/skyblocker/utils/scheduler/SchedulerTest.java b/src/test/java/me/xmrvizzy/skyblocker/utils/scheduler/SchedulerTest.java deleted file mode 100644 index 5b3317ab..00000000 --- a/src/test/java/me/xmrvizzy/skyblocker/utils/scheduler/SchedulerTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.xmrvizzy.skyblocker.utils.scheduler; - -import org.apache.commons.lang3.mutable.MutableInt; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; - -public class SchedulerTest { - private final MutableInt currentTick = new MutableInt(0); - private final MutableInt cycleCount1 = new MutableInt(0); - private final MutableInt cycleCount2 = new MutableInt(0); - private final MutableInt cycleCount3 = new MutableInt(0); - private final MutableInt cycleCount4 = new MutableInt(0); - private final MutableInt cycleCount5 = new MutableInt(0); - private final MutableInt cycleCount6 = new MutableInt(0); - private final MutableInt cycleCount7 = new MutableInt(0); - private final MutableInt cycleCount8 = new MutableInt(0); - - @Test - public void testSchedule() { - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(0, currentTick.intValue()), 0); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(1, currentTick.intValue()), 1); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(2, currentTick.intValue()), 2); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(10, currentTick.intValue()), 10); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(20, currentTick.intValue()), 20); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(50, currentTick.intValue()), 50); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(100, currentTick.intValue()), 100); - Scheduler.INSTANCE.schedule(() -> Assertions.assertEquals(123, currentTick.intValue()), 123); - Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); - Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); - Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); - Scheduler.INSTANCE.scheduleCyclic(() -> {}, 1); - Scheduler.INSTANCE.scheduleCyclic(() -> { - Assertions.assertEquals(cycleCount1.intValue(), currentTick.intValue()); - cycleCount1.increment(); - }, 1); - Scheduler.INSTANCE.scheduleCyclic(() -> { - Assertions.assertEquals(0, currentTick.intValue() % 10); - Assertions.assertEquals(cycleCount2.intValue(), currentTick.intValue() / 10); - cycleCount2.increment(); - }, 10); - Scheduler.INSTANCE.scheduleCyclic(() -> { - Assertions.assertEquals(0, currentTick.intValue() % 55); - Assertions.assertEquals(cycleCount3.intValue(), currentTick.intValue() / 55); - cycleCount3.increment(); - }, 55); - Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(() -> { - Assertions.assertEquals(7, currentTick.intValue() % 10); - Assertions.assertEquals(cycleCount4.intValue(), currentTick.intValue() / 10); - cycleCount4.increment(); - }, 10), 7); - Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(() -> { - Assertions.assertEquals(0, currentTick.intValue() % 75); - Assertions.assertEquals(cycleCount5.intValue(), currentTick.intValue() / 75); - cycleCount5.increment(); - }, 75), 0); - Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(() -> { - Assertions.assertEquals(1, currentTick.intValue() % 99); - Assertions.assertEquals(cycleCount6.intValue(), currentTick.intValue() / 99); - cycleCount6.increment(); - }, 99), 1); - Scheduler.INSTANCE.scheduleCyclic(() -> Scheduler.INSTANCE.schedule(() -> { - Assertions.assertEquals(5, currentTick.intValue() % 10); - Assertions.assertEquals(cycleCount7.intValue(), currentTick.intValue() / 10); - cycleCount7.increment(); - }, 5), 10); - Scheduler.INSTANCE.scheduleCyclic(() -> Scheduler.INSTANCE.schedule(() -> { - Assertions.assertEquals(10, currentTick.intValue() % 55); - Assertions.assertEquals(cycleCount8.intValue(), currentTick.intValue() / 55); - cycleCount8.increment(); - }, 10), 55); - while (currentTick.intValue() < 100_000) { - tick(); - } - Assertions.assertEquals(100000, cycleCount1.intValue()); - Assertions.assertEquals(10000, cycleCount2.intValue()); - Assertions.assertEquals(1819, cycleCount3.intValue()); - Assertions.assertEquals(10000, cycleCount4.intValue()); - Assertions.assertEquals(1334, cycleCount5.intValue()); - Assertions.assertEquals(1011, cycleCount6.intValue()); - Assertions.assertEquals(10000, cycleCount7.intValue()); - Assertions.assertEquals(1818, cycleCount8.intValue()); - } - - private void tick() { - Scheduler.INSTANCE.tick(); - currentTick.increment(); - } -} -- cgit From ba30ca8fac0c92482875f93718fef253dd5673db Mon Sep 17 00:00:00 2001 From: Yasin <a.piri@hotmail.de> Date: Mon, 9 Oct 2023 20:20:09 +0200 Subject: fix stuff intellij introduced --- src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java | 10 ++++++---- .../de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java | 3 +-- .../skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java | 2 +- .../skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java | 3 ++- src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java | 3 +-- .../de/hysky/skyblocker/utils/discord/DiscordRPCManager.java | 11 +++++------ 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java index c7f5fac9..76073a2c 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixin/ItemStackMixin.java @@ -1,6 +1,6 @@ package de.hysky.skyblocker.mixin; -import de.hysky.skyblocker.utils.ItemUtils; + import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -9,6 +9,8 @@ import org.spongepowered.asm.mixin.injection.At; import com.llamalad7.mixinextras.injector.ModifyReturnValue; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.ItemUtils.Durability; import de.hysky.skyblocker.utils.Utils; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; @@ -34,7 +36,7 @@ public abstract class ItemStackMixin { @ModifyReturnValue(method = "getDamage", at = @At("RETURN")) private int skyblocker$handleDamage(int original) { - ItemUtils.Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); if (dur != null) { return dur.max() - dur.current(); } @@ -43,7 +45,7 @@ public abstract class ItemStackMixin { @ModifyReturnValue(method = "getMaxDamage", at = @At("RETURN")) private int skyblocker$handleMaxDamage(int original) { - ItemUtils.Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); if (dur != null) { return dur.max(); } @@ -52,7 +54,7 @@ public abstract class ItemStackMixin { @ModifyReturnValue(method = "isDamageable", at = @At("RETURN")) private boolean skyblocker$handleDamageable(boolean original) { - ItemUtils.Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); + Durability dur = ItemUtils.getDurability((ItemStack) (Object) this); if (dur != null) { return true; } diff --git a/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java b/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java index 3a9e688b..dd4f5ef1 100644 --- a/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java +++ b/src/main/java/de/hysky/skyblocker/mixin/accessor/FrustumInvoker.java @@ -1,12 +1,11 @@ package de.hysky.skyblocker.mixin.accessor; -import de.hysky.skyblocker.utils.render.FrustumUtils; import net.minecraft.client.render.Frustum; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Invoker; /** - * Use {@link FrustumUtils#isVisible(double, double, double, double, double, double) FrustumUtils#isVisible} which is shorter. For the purpose of avoiding object allocations! + * Use {@link de.hysky.skyblocker.utils.render.FrustumUtils#isVisible(double, double, double, double, double, double) FrustumUtils#isVisible} which is shorter. For the purpose of avoiding object allocations! */ @Mixin(Frustum.class) public interface FrustumInvoker { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index 7d20644a..c2358689 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -430,7 +430,7 @@ public class DungeonSecrets { } /** - * Checks if the player is in a dungeon and {@link SkyblockerConfigManager.Dungeons#secretWaypoints Secret Waypoints} is enabled. + * Checks if the player is in a dungeon and {@link de.hysky.skyblocker.config.SkyblockerConfig.Dungeons#secretWaypoints Secret Waypoints} is enabled. * * @return whether dungeon secrets should be processed */ diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java index 7b62221e..2bb21568 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java @@ -1,6 +1,7 @@ package de.hysky.skyblocker.skyblock.dwarven; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud.Commission; import de.hysky.skyblocker.skyblock.tabhud.widget.hud.HudCommsWidget; import de.hysky.skyblocker.utils.render.RenderHelper; import it.unimi.dsi.fastutil.ints.IntIntPair; @@ -13,7 +14,7 @@ import java.util.List; public class DwarvenHudConfigScreen extends Screen { - private static final List<DwarvenHud.Commission> CFG_COMMS = List.of(new DwarvenHud.Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")); + private static final List<DwarvenHud.Commission> CFG_COMMS = List.of(new Commission("Test Commission 1", "1%"), new DwarvenHud.Commission("Test Commission 2", "2%")); private int hudX = SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.x; private int hudY = SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.y; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java index 10b593bd..b39151d3 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java @@ -1,13 +1,12 @@ package de.hysky.skyblocker.skyblock.rift; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; public class TheRift { /** - * @see Utils#isInTheRift() Utils#isInTheRift(). + * @see de.hysky.skyblocker.utils.Utils#isInTheRift() Utils#isInTheRift(). */ public static final String LOCATION = "rift"; diff --git a/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java b/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java index f0589a0b..4f48a12f 100644 --- a/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/discord/DiscordRPCManager.java @@ -1,7 +1,6 @@ package de.hysky.skyblocker.utils.discord; -import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.events.SkyblockEvents; import de.hysky.skyblocker.utils.Utils; @@ -35,7 +34,7 @@ public class DiscordRPCManager { } /** - * Checks the {@link SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. + * Checks the {@link de.hysky.skyblocker.config.SkyblockerConfig.RichPresence#customMessage custom message}, updates {@link #cycleCount} if enabled, and updates rich presence. */ public static void updateDataAndPresence() { // If the custom message is empty, discord will keep the last message, this is can serve as a default if the user doesn't want a custom message @@ -59,16 +58,16 @@ public class DiscordRPCManager { * <p> * When the {@link #updateTask previous update} does not exist or {@link CompletableFuture#isDone() has completed}: * <p> - * Connects to discord if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}, + * Connects to discord if {@link de.hysky.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled}, * the player {@link Utils#isOnSkyblock() is on Skyblock}, and {@link DiscordIPC#isConnected() discord is not already connected}. - * Updates the presence if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled} + * Updates the presence if {@link de.hysky.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is enabled} * and the player {@link Utils#isOnSkyblock() is on Skyblock}. - * Stops the connection if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled} + * Stops the connection if {@link de.hysky.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled} * or the player {@link Utils#isOnSkyblock() is not on Skyblock} and {@link DiscordIPC#isConnected() discord is connected}. * Saves the update task in {@link #updateTask} * * @param initialization whether this is the first time the presence is being updates. If {@code true}, a message will be logged - * if {@link SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}. + * if {@link de.hysky.skyblocker.config.SkyblockerConfig.RichPresence#enableRichPresence rich presence is disabled}. */ private static void initAndUpdatePresence(boolean initialization) { if (updateTask == null || updateTask.isDone()) { -- cgit From f346617971b20f6be373411ce5433487cd8d82c2 Mon Sep 17 00:00:00 2001 From: Yasin <a.piri@hotmail.de> Date: Mon, 9 Oct 2023 20:33:31 +0200 Subject: fix the forgotten --- src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java | 4 ++-- src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java | 4 ++-- .../java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java | 4 ++-- .../java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java | 4 ++-- .../java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java | 2 +- .../java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java index db10e952..17842891 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/AbilityFilter.java @@ -1,7 +1,7 @@ package de.hysky.skyblocker.skyblock.filters; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; public class AbilityFilter extends SimpleChatFilter { public AbilityFilter() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java index 371615b8..8b46efb5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/HealFilter.java @@ -1,7 +1,7 @@ package de.hysky.skyblocker.skyblock.filters; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; public class HealFilter extends SimpleChatFilter { public HealFilter() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java index 454d7b78..730f6d5d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/ImplosionFilter.java @@ -1,7 +1,7 @@ package de.hysky.skyblocker.skyblock.filters; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; public class ImplosionFilter extends SimpleChatFilter { public ImplosionFilter() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java index afc15a2c..aa3bb64d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/MoltenWaveFilter.java @@ -1,7 +1,7 @@ package de.hysky.skyblocker.skyblock.filters; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; -import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.utils.chat.ChatFilterResult; public class MoltenWaveFilter extends SimpleChatFilter { public MoltenWaveFilter() { diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java index 6a4d96d3..05f0afa5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/ScreenConst.java @@ -1,6 +1,6 @@ package de.hysky.skyblocker.skyblock.tabhud.util; -import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.SkyblockerConfigManager; public class ScreenConst { public static final int WIDGET_PAD = 5; diff --git a/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java b/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java index 7b779945..b8b08016 100644 --- a/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java +++ b/src/test/java/de/hysky/skyblocker/skyblock/filters/ChatFilterTest.java @@ -1,7 +1,7 @@ package de.hysky.skyblocker.skyblock.filters; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener; -import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListenerTest; +import de.hysky.skyblocker.utils.chat.ChatPatternListener; +import de.hysky.skyblocker.utils.chat.ChatPatternListenerTest; public class ChatFilterTest<T extends ChatPatternListener> extends ChatPatternListenerTest<T> { public ChatFilterTest(T listener) { -- cgit