aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java22
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java15
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java11
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java8
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/ClientWorldMixin.java12
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java11
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/PlayerSkinTextureMixin.java51
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/bazaar/BazaarHelper.java73
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/bazaar/ReorderHelper.java73
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java41
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/LightsOn.java46
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/SimonSays.java122
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java21
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java32
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java25
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotText.java19
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotTextManager.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/CollectionAdder.java7
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/SkillLevelAdder.java9
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipAdder.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/itemLoaders/ItemLoader.java10
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java10
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java46
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/ItemUtils.java47
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/TextTransformer.java92
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/EggTypeArgumentType.java40
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientBlockPosArgumentType.java72
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientPosArgument.java27
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/DefaultClientPosArgument.java113
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/LookingClientPosArgument.java106
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/ColorArgumentType.java28
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/HexColorArgumentType.java38
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/RgbColorArgumentType.java39
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java20
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java5
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json18
-rw-r--r--src/main/resources/skyblocker.accesswidener1
46 files changed, 1206 insertions, 147 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index f1eb4321..8dd1419d 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -14,6 +14,8 @@ import de.hysky.skyblocker.skyblock.chocolatefactory.TimeTowerReminder;
import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager;
import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra;
import de.hysky.skyblocker.skyblock.dungeon.*;
+import de.hysky.skyblocker.skyblock.dungeon.device.LightsOn;
+import de.hysky.skyblocker.skyblock.dungeon.device.SimonSays;
import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.*;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.boulder.Boulder;
@@ -151,6 +153,8 @@ public class SkyblockerMod implements ClientModInitializer {
Silverfish.init();
IceFill.init();
DungeonScore.init();
+ SimonSays.init();
+ LightsOn.init();
PartyFinderScreen.initClass();
ChestValue.init();
FireFreezeStaffTimer.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
index 28ace441..017e9186 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
@@ -269,6 +269,28 @@ public class DungeonsCategory {
.build())
.build())
+ // Devices (F7/M7)
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("skyblocker.config.dungeons.devices"))
+ .collapsed(true)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.dungeons.devices.solveSimonSays"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.dungeons.devices.solveSimonSays.@Tooltip")))
+ .binding(defaults.dungeons.devices.solveSimonSays,
+ () -> config.dungeons.devices.solveSimonSays,
+ newValue -> config.dungeons.devices.solveSimonSays = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.dungeons.devices.solveLightsOn"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.dungeons.devices.solveLightsOn.@Tooltip")))
+ .binding(defaults.dungeons.devices.solveLightsOn,
+ () -> config.dungeons.devices.solveLightsOn,
+ newValue -> config.dungeons.devices.solveLightsOn = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .build())
+
// Dungeon Secret Waypoints
.group(OptionGroup.createBuilder()
.name(Text.translatable("skyblocker.config.dungeons.secretWaypoints"))
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java
index 56dbca94..ec2c561c 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java
@@ -2,6 +2,7 @@ package de.hysky.skyblocker.config.categories;
import de.hysky.skyblocker.config.ConfigUtils;
import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.skyblock.bazaar.BazaarHelper;
import de.hysky.skyblocker.utils.waypoint.Waypoint;
import dev.isxander.yacl3.api.ConfigCategory;
import dev.isxander.yacl3.api.Option;
@@ -206,6 +207,20 @@ public class HelperCategory {
.build())
.build())
+ //Bazaar
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("skyblocker.config.helpers.bazaar"))
+ .collapsed(true)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("skyblocker.config.helpers.bazaar.enableBazaarHelper"))
+ .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.bazaar.enableBazaarHelper.@Tooltip", BazaarHelper.getExpiringIcon(), BazaarHelper.getExpiredIcon(), BazaarHelper.getFilledIcon(69), BazaarHelper.getFilledIcon(100))))
+ .binding(defaults.helpers.bazaar.enableBazaarHelper,
+ () -> config.helpers.bazaar.enableBazaarHelper,
+ newValue -> config.helpers.bazaar.enableBazaarHelper = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .build())
+
.build();
}
}
diff --git a/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java
index 7b394b53..1a0cad9d 100644
--- a/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/configs/DungeonsConfig.java
@@ -42,6 +42,9 @@ public class DungeonsConfig {
public Terminals terminals = new Terminals();
@SerialEntry
+ public Devices devices = new Devices();
+
+ @SerialEntry
public SecretWaypoints secretWaypoints = new SecretWaypoints();
@SerialEntry
@@ -135,6 +138,14 @@ public class DungeonsConfig {
public boolean blockIncorrectClicks = false;
}
+ public static class Devices {
+ @SerialEntry
+ public boolean solveSimonSays = true;
+
+ @SerialEntry
+ public boolean solveLightsOn = true;
+ }
+
public static class SecretWaypoints {
@SerialEntry
public boolean enableRoomMatching = true;
diff --git a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java
index 636d76da..e009f680 100644
--- a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java
@@ -26,6 +26,9 @@ public class HelperConfig {
@SerialEntry
public ChocolateFactory chocolateFactory = new ChocolateFactory();
+ @SerialEntry
+ public Bazaar bazaar = new Bazaar();
+
public static class MythologicalRitual {
@SerialEntry
public boolean enableMythologicalRitualHelper = true;
@@ -94,4 +97,9 @@ public class HelperConfig {
@SerialEntry
public boolean straySound = true;
}
+
+ public static class Bazaar {
+ @SerialEntry
+ public boolean enableBazaarHelper = true;
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientWorldMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientWorldMixin.java
index 6c10e5d2..a2d7887b 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/ClientWorldMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/ClientWorldMixin.java
@@ -1,7 +1,7 @@
package de.hysky.skyblocker.mixins;
-
import de.hysky.skyblocker.skyblock.crimson.dojo.DojoManager;
+import de.hysky.skyblocker.skyblock.dungeon.device.SimonSays;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.block.BlockState;
import net.minecraft.client.world.ClientWorld;
@@ -11,13 +11,21 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import com.llamalad7.mixinextras.sugar.Local;
+
@Mixin(ClientWorld.class)
public class ClientWorldMixin {
+ /**
+ * @implNote The {@code pos} can be mutable when this is called by chunk delta updates, so if you want to copy it into memory
+ * (e.g. store it in a field/list/map) make sure to duplicate it via {@link BlockPos#toImmutable()}.
+ */
@Inject(method = "handleBlockUpdate", at = @At("RETURN"))
- private void skyblocker$handleBlockUpdate(BlockPos pos, BlockState state, int flags, CallbackInfo ci) {
+ private void skyblocker$handleBlockUpdate(CallbackInfo ci, @Local(argsOnly = true) BlockPos pos, @Local(argsOnly = true) BlockState state) {
if (Utils.isInCrimson()) {
DojoManager.onBlockUpdate(pos.toImmutable(), state);
}
+
+ SimonSays.onBlockUpdate(pos, state);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
index f2e3e907..709b8697 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
@@ -247,10 +247,21 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
return;
}
// Prevent salvaging
+ // TODO in future maybe also block clicking the salvage button if a protected item manages to get into the menu
if (title.equals("Salvage Items") && ItemProtection.isItemProtected(stack)) {
ci.cancel();
return;
}
+ // Prevent Trading
+ if (title.startsWith("You ") && ItemProtection.isItemProtected(stack)) { //Terrible way to detect the trade menu lol
+ ci.cancel();
+ return;
+ }
+ // Prevent Auctioning
+ if ((title.equals("Create BIN Auction") || title.equals("Create Auction")) && ItemProtection.isItemProtected(stack)) {
+ ci.cancel();
+ return;
+ }
switch (this.handler) {
case GenericContainerScreenHandler genericContainerScreenHandler when genericContainerScreenHandler.getRows() == 6 -> {
diff --git a/src/main/java/de/hysky/skyblocker/mixins/PlayerSkinTextureMixin.java b/src/main/java/de/hysky/skyblocker/mixins/PlayerSkinTextureMixin.java
index 828d32e3..9b9691c5 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/PlayerSkinTextureMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/PlayerSkinTextureMixin.java
@@ -1,5 +1,8 @@
package de.hysky.skyblocker.mixins;
+import java.awt.Color;
+import java.util.Set;
+
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -9,32 +12,62 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.llamalad7.mixinextras.injector.v2.WrapWithCondition;
+import com.llamalad7.mixinextras.sugar.Share;
+import com.llamalad7.mixinextras.sugar.ref.LocalBooleanRef;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.item.PlayerHeadHashCache;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.texture.PlayerSkinTexture;
+import net.minecraft.util.math.ColorHelper;
@Mixin(PlayerSkinTexture.class)
public class PlayerSkinTextureMixin {
+ @Unique
+ private static final Set<String> STRIP_DE_FACTO_TRANSPARENT_PIXELS = Set.of(
+ "4f3b91b6aa7124f30ed4ad1b2bb012a82985a33640555e18e792f96af8f58ec6", /*Titanium Necklace*/
+ "49821410631186c6f3fbbae5f0ef5b947f475eb32027a8aad0a456512547c209", /*Titanium Cloak*/
+ "4162303bcdd770aebe7fd19fa26371390a7515140358548084361b5056cdc4e6" /*Titanium Belt*/);
+ @Unique
+ private static final float BRIGHTNESS_THRESHOLD = 0.1f;
+
@Shadow
@Final
private String url;
- @Unique
- private boolean isSkyblockSkinTexture;
-
@Inject(method = "remapTexture", at = @At("HEAD"))
- private void skyblocker$determineSkinSource(CallbackInfoReturnable<NativeImage> cir) {
- if (Utils.isOnSkyblock()) {
- int skinHash = PlayerHeadHashCache.getSkinHash(this.url).hashCode();
- this.isSkyblockSkinTexture = PlayerHeadHashCache.contains(skinHash);
+ private void skyblocker$determineSkinSource(NativeImage image, CallbackInfoReturnable<NativeImage> cir, @Share("isSkyblockSkinTexture") LocalBooleanRef isSkyblockSkinTexture) {
+ if (Utils.isOnSkyblock() && SkyblockerConfigManager.get().uiAndVisuals.dontStripSkinAlphaValues) {
+ String skinTextureHash = PlayerHeadHashCache.getSkinHash(this.url);
+ int skinHash = skinTextureHash.hashCode();
+ isSkyblockSkinTexture.set(PlayerHeadHashCache.contains(skinHash));
+
+ //Hypixel had the grand idea of using black pixels in place of actual transparent pixels on the titanium equipment so here we go!
+ if (STRIP_DE_FACTO_TRANSPARENT_PIXELS.contains(skinTextureHash)) {
+ stripDeFactoTransparentPixels(image);
+ }
}
}
@WrapWithCondition(method = "remapTexture", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/texture/PlayerSkinTexture;stripAlpha(Lnet/minecraft/client/texture/NativeImage;IIII)V"))
- private boolean skyblocker$dontStripAlphaValues(NativeImage image, int x1, int y1, int x2, int y2) {
- return !(SkyblockerConfigManager.get().uiAndVisuals.dontStripSkinAlphaValues && this.isSkyblockSkinTexture);
+ private boolean skyblocker$dontStripAlphaValues(NativeImage image, int x1, int y1, int x2, int y2, @Share("isSkyblockSkinTexture") LocalBooleanRef isSkyblockSkinTexture) {
+ return !isSkyblockSkinTexture.get();
+ }
+
+ @Unique
+ private static void stripDeFactoTransparentPixels(NativeImage image) {
+ int height = image.getHeight();
+ int width = image.getWidth();
+
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ int color = image.getColor(x, y);
+ float[] hsb = Color.RGBtoHSB(ColorHelper.Abgr.getRed(color), ColorHelper.Abgr.getGreen(color), ColorHelper.Abgr.getBlue(color), null);
+
+ //Work around "fake" transparent pixels - Thanks Hypixel I totally appreciate this!
+ if (hsb[2] <= BRIGHTNESS_THRESHOLD) image.setColor(x, y, ColorHelper.Abgr.withAlpha(0x00, color & 0x00FFFFFF));
+ }
+ }
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/bazaar/BazaarHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/bazaar/BazaarHelper.java
new file mode 100644
index 00000000..8b83b06b
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/bazaar/BazaarHelper.java
@@ -0,0 +1,73 @@
+package de.hysky.skyblocker.skyblock.bazaar;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.slottext.SlotText;
+import de.hysky.skyblocker.skyblock.item.slottext.SlotTextAdder;
+import de.hysky.skyblocker.utils.ItemUtils;
+import net.minecraft.item.ItemStack;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.apache.commons.lang3.math.NumberUtils;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BazaarHelper extends SlotTextAdder {
+ private static final Pattern FILLED_PATTERN = Pattern.compile("Filled: \\S+ \\(?([\\d.]+)%\\)?!?");
+ private static final int RED = 0xe60b1e;
+ private static final int YELLOW = 0xe6ba0b;
+ private static final int GREEN = 0x1ee60b;
+
+ public BazaarHelper() {
+ super("(?:Co-op|Your) Bazaar Orders");
+ }
+
+ @Override
+ public @NotNull List<SlotText> getText(Slot slot) {
+ if (!SkyblockerConfigManager.get().helpers.bazaar.enableBazaarHelper) return List.of();
+ // Skip the first row as it's always glass panes.
+ if (slot.id < 10) return List.of();
+ // Skip the last 10 items. 11 is subtracted because size is 1-based so the last slot is size - 1.
+ if (slot.id > slot.inventory.size() - 11) return List.of(); //Note that this also skips the slots in player's inventory (anything above 36/45/54 depending on the order count)
+
+ int column = slot.id % 9;
+ if (column == 0 || column == 8) return List.of(); // Skip the first and last column as those are always glass panes as well.
+
+ ItemStack item = slot.getStack();
+ if (item.isEmpty()) return List.of(); //We've skipped all invalid slots, so we can just check if it's not air here.
+
+ Matcher matcher = ItemUtils.getLoreLineIfMatch(item, FILLED_PATTERN);
+ if (matcher != null) {
+ List<Text> lore = ItemUtils.getLore(item);
+ if (!lore.isEmpty() && lore.getLast().getString().equals("Click to claim!")) { //Only show the filled icon when there are items to claim
+ int filled = NumberUtils.toInt(matcher.group(1));
+ return SlotText.topLeftList(getFilledIcon(filled));
+ }
+ }
+
+ if (ItemUtils.getLoreLineIf(item, str -> str.equals("Expired!")) != null) {
+ return SlotText.topLeftList(getExpiredIcon());
+ } else if (ItemUtils.getLoreLineIf(item, str -> str.startsWith("Expires in")) != null) {
+ return SlotText.topLeftList(getExpiringIcon());
+ }
+
+ return List.of();
+ }
+
+ public static @NotNull MutableText getExpiredIcon() {
+ return Text.literal("⏰").withColor(RED).formatted(Formatting.BOLD);
+ }
+
+ public static @NotNull MutableText getExpiringIcon() {
+ return Text.literal("⏰").withColor(YELLOW).formatted(Formatting.BOLD);
+ }
+
+ public static @NotNull MutableText getFilledIcon(int filled) {
+ if (filled < 100) return Text.literal("%").withColor(YELLOW).formatted(Formatting.BOLD);
+ return Text.literal("✅").withColor(GREEN).formatted(Formatting.BOLD);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/bazaar/ReorderHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/bazaar/ReorderHelper.java
new file mode 100644
index 00000000..c2b11926
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/bazaar/ReorderHelper.java
@@ -0,0 +1,73 @@
+package de.hysky.skyblocker.skyblock.bazaar;
+
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipAdder;
+import de.hysky.skyblocker.utils.ItemUtils;
+import de.hysky.skyblocker.utils.render.gui.ColorHighlight;
+import de.hysky.skyblocker.utils.render.gui.ContainerSolver;
+import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.util.InputUtil;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.Items;
+import net.minecraft.screen.slot.Slot;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.jetbrains.annotations.Nullable;
+import org.lwjgl.glfw.GLFW;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ReorderHelper extends ContainerSolver {
+ private static final Pattern BUY_PATTERN = Pattern.compile("([\\d,]+)x missing items\\.");
+ private static final Pattern SELL_PATTERN = Pattern.compile("([\\d,]+)x items\\.");
+
+ public ReorderHelper() {
+ super("^Order options");
+ }
+
+ @Override
+ protected boolean isEnabled() {
+ return true;
+ }
+
+ @Override
+ protected boolean onClickSlot(int slot, ItemStack stack, int screenId, String[] groups) {
+ // V This part is so that it short-circuits if not necessary
+ if ((slot == 11 || slot == 13) && stack.isOf(Items.GREEN_TERRACOTTA) && InputUtil.isKeyPressed(MinecraftClient.getInstance().getWindow().getHandle(), GLFW.GLFW_KEY_LEFT_CONTROL)) {
+ Matcher matcher;
+ // The terracotta is at slot 13 on sell orders and at slot 11 on buy orders
+ if (slot == 13) matcher = ItemUtils.getLoreLineIfContainsMatch(stack, SELL_PATTERN);
+ else matcher = ItemUtils.getLoreLineIfContainsMatch(stack, BUY_PATTERN);
+ if (matcher != null) {
+ MinecraftClient.getInstance().keyboard.setClipboard(matcher.group(1).replace(",", ""));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected List<ColorHighlight> getColors(String[] groups, Int2ObjectMap<ItemStack> slots) {
+ return List.of();
+ }
+
+ public static class Tooltip extends TooltipAdder {
+ public Tooltip() {
+ super("^Order options", Integer.MIN_VALUE);
+ }
+
+ @Override
+ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List<Text> lines) {
+ if (focusedSlot == null || !stack.isOf(Items.GREEN_TERRACOTTA)) return;
+ switch (focusedSlot.id) {
+ case 11, 13 -> {
+ lines.add(Text.empty());
+ lines.add(Text.empty().append(Text.translatable("skyblocker.reorderHelper.tooltip.line1")).formatted(Formatting.DARK_GRAY, Formatting.ITALIC));
+ lines.add(Text.empty().append(Text.translatable("skyblocker.reorderHelper.tooltip.line2")).formatted(Formatting.DARK_GRAY, Formatting.ITALIC));
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java
index 620da37c..6926fda8 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java
@@ -1,16 +1,16 @@
package de.hysky.skyblocker.skyblock.chocolatefactory;
import com.mojang.brigadier.Command;
-import com.mojang.brigadier.arguments.IntegerArgumentType;
-import com.mojang.brigadier.arguments.StringArgumentType;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.events.SkyblockEvents;
import de.hysky.skyblocker.utils.*;
+import de.hysky.skyblocker.utils.command.argumenttypes.EggTypeArgumentType;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientPosArgument;
import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
import de.hysky.skyblocker.utils.waypoint.Waypoint;
import it.unimi.dsi.fastutil.objects.ObjectImmutableList;
-import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
@@ -24,6 +24,7 @@ import net.minecraft.text.ClickEvent;
import net.minecraft.text.HoverEvent;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
+import org.apache.commons.lang3.text.WordUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -31,6 +32,9 @@ import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
public class EggFinder {
private static final Pattern eggFoundPattern = Pattern.compile("^(?:HOPPITY'S HUNT You found a Chocolate|You have already collected this Chocolate) (Breakfast|Lunch|Dinner)");
private static final Pattern newEggPattern = Pattern.compile("^HOPPITY'S HUNT A Chocolate (Breakfast|Lunch|Dinner) Egg has appeared!$");
@@ -47,17 +51,15 @@ public class EggFinder {
SkyblockEvents.LOCATION_CHANGE.register(EggFinder::handleLocationChange);
ClientReceiveMessageEvents.GAME.register(EggFinder::onChatMessage);
WorldRenderEvents.AFTER_TRANSLUCENT.register(EggFinder::renderWaypoints);
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal(SkyblockerMod.NAMESPACE)
- .then(ClientCommandManager.literal("eggFinder")
- .then(ClientCommandManager.literal("shareLocation")
- .then(ClientCommandManager.argument("x", IntegerArgumentType.integer())
- .then(ClientCommandManager.argument("y", IntegerArgumentType.integer())
- .then(ClientCommandManager.argument("z", IntegerArgumentType.integer())
- .then(ClientCommandManager.argument("eggType", StringArgumentType.word())
- .executes(context -> {
- MessageScheduler.INSTANCE.sendMessageAfterCooldown("[Skyblocker] Chocolate " + context.getArgument("eggType", String.class) + " Egg found at " + context.getArgument("x", Integer.class) + " " + context.getArgument("y", Integer.class) + " " + context.getArgument("z", Integer.class) + "!");
- return Command.SINGLE_SUCCESS;
- })))))))));
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE)
+ .then(literal("eggFinder")
+ .then(literal("shareLocation")
+ .then(argument("blockPos", ClientBlockPosArgumentType.blockPos())
+ .then(argument("eggType", EggTypeArgumentType.eggType())
+ .executes(context -> {
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown("[Skyblocker] Chocolate " + context.getArgument("eggType", EggType.class) + " Egg found at " + context.getArgument("blockPos", ClientPosArgument.class).toAbsoluteBlockPos(context.getSource()).toShortString() + "!");
+ return Command.SINGLE_SUCCESS;
+ })))))));
}
private static void handleLocationChange(Location location) {
@@ -161,7 +163,7 @@ public class EggFinder {
record Egg(ArmorStandEntity entity, Waypoint waypoint) {}
@SuppressWarnings("DataFlowIssue") //Removes that pesky "unboxing of Integer might cause NPE" warning when we already know it's not null
- enum EggType {
+ public enum EggType {
LUNCH(Formatting.BLUE.getColorValue(), "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9"),
DINNER(Formatting.GREEN.getColorValue(), "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0"),
BREAKFAST(Formatting.GOLD.getColorValue(), "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0");
@@ -179,7 +181,7 @@ public class EggFinder {
private long messageLastSent = 0;
//This is to not create an array each time we iterate over the values
- public static final ObjectImmutableList<EggType> entries = ObjectImmutableList.of(BREAKFAST, LUNCH, DINNER);
+ public static final ObjectImmutableList<EggType> entries = ObjectImmutableList.of(EggType.values());
EggType(int color, String texture) {
this.color = color;
@@ -187,12 +189,9 @@ public class EggFinder {
}
@Override
+ @SuppressWarnings("deprecation") // It's either a new dependency or a deprecated method, and I'd rather use the deprecated method
public String toString() {
- return switch (this) {
- case LUNCH -> "Lunch";
- case DINNER -> "Dinner";
- case BREAKFAST -> "Breakfast";
- };
+ return WordUtils.capitalizeFully(this.name());
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/LightsOn.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/LightsOn.java
new file mode 100644
index 00000000..555a8e4b
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/LightsOn.java
@@ -0,0 +1,46 @@
+package de.hysky.skyblocker.skyblock.dungeon.device;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.dungeon.DungeonBoss;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.utils.ColorUtils;
+import de.hysky.skyblocker.utils.Utils;
+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.block.Blocks;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.state.property.Properties;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+
+public class LightsOn {
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+ private static final BlockPos TOP_LEFT = new BlockPos(62, 136, 142);
+ private static final BlockPos TOP_RIGHT = new BlockPos(58, 136, 142);
+ private static final BlockPos MIDDLE_TOP = new BlockPos(60, 135, 142);
+ private static final BlockPos MIDDLE_BOTTOM = new BlockPos(60, 134, 142);
+ private static final BlockPos BOTTOM_LEFT = new BlockPos(62, 133, 142);
+ private static final BlockPos BOTTOM_RIGHT = new BlockPos(58, 133, 142);
+ private static final BlockPos[] LEVERS = { TOP_LEFT, TOP_RIGHT, MIDDLE_TOP, MIDDLE_BOTTOM, BOTTOM_LEFT, BOTTOM_RIGHT };
+ private static final float[] RED = ColorUtils.getFloatComponents(DyeColor.RED);
+
+ public static void init() {
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(LightsOn::render);
+ }
+
+ private static void render(WorldRenderContext context) {
+ if (SkyblockerConfigManager.get().dungeons.devices.solveLightsOn && Utils.isInDungeons() && DungeonManager.isInBoss() && DungeonManager.getBoss() == DungeonBoss.MAXOR) {
+ for (BlockPos lever : LEVERS) {
+ ClientWorld world = CLIENT.world;
+ BlockState state = world.getBlockState(lever);
+
+ if (state.getBlock().equals(Blocks.LEVER) && state.contains(Properties.POWERED) && !state.get(Properties.POWERED)) {
+ RenderHelper.renderFilled(context, lever, RED, 0.5f, false);
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/SimonSays.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/SimonSays.java
new file mode 100644
index 00000000..5aa97dd9
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/device/SimonSays.java
@@ -0,0 +1,122 @@
+package de.hysky.skyblocker.skyblock.dungeon.device;
+
+import java.util.Objects;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.dungeon.DungeonBoss;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.utils.Boxes;
+import de.hysky.skyblocker.utils.ColorUtils;
+import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import it.unimi.dsi.fastutil.objects.ObjectList;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import it.unimi.dsi.fastutil.objects.ObjectSet;
+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.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.Hand;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+
+public class SimonSays {
+ private static final Box BOARD_AREA = Box.enclosing(new BlockPos(111, 123, 92), new BlockPos(111, 120, 95));
+ private static final Box BUTTONS_AREA = Box.enclosing(new BlockPos(110, 123, 92), new BlockPos(110, 120, 95));
+ private static final BlockPos START_BUTTON = new BlockPos(110, 121, 91);
+ private static final float[] GREEN = ColorUtils.getFloatComponents(DyeColor.LIME);
+ private static final float[] YELLOW = ColorUtils.getFloatComponents(DyeColor.YELLOW);
+ private static final ObjectSet<BlockPos> CLICKED_BUTTONS = new ObjectOpenHashSet<>();
+ private static final ObjectList<BlockPos> SIMON_PATTERN = new ObjectArrayList<>();
+
+ public static void init() {
+ UseBlockCallback.EVENT.register(SimonSays::onBlockInteract);
+ ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset());
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(SimonSays::render);
+ }
+
+ //When another player is pressing the buttons hypixel doesnt send block or block state updates
+ //so you can't see it which means the solver can only count the buttons you press yourself
+ private static ActionResult onBlockInteract(PlayerEntity player, World world, Hand hand, BlockHitResult hitResult) {
+ if (shouldProcess()) {
+ BlockPos pos = hitResult.getBlockPos();
+ Block block = world.getBlockState(pos).getBlock();
+
+ if (block.equals(Blocks.STONE_BUTTON)) {
+ if (BUTTONS_AREA.contains(Vec3d.of(pos))) {
+ CLICKED_BUTTONS.add(new BlockPos(pos)); //Copy just in case it becomes mutable in the future
+ } else if (pos.equals(START_BUTTON)) {
+ reset();
+ }
+ }
+ }
+
+ //This could also be used to cancel incorrect clicks in the future
+ return ActionResult.PASS;
+ }
+
+ //If the player goes out of the range required to receive block/chunk updates then their solver won't detect stuff but that
+ //doesn't matter because if they're doing pre-4 or something they won't be doing the ss, and if they end up needing to they can
+ //just reset it or have the other person finish the current sequence first then let them do it.
+ public static void onBlockUpdate(BlockPos pos, BlockState state) {
+ if (shouldProcess()) {
+ Vec3d posVec = Vec3d.of(pos);
+ Block block = state.getBlock();
+
+ if (BOARD_AREA.contains(posVec) && block.equals(Blocks.SEA_LANTERN)) {
+ SIMON_PATTERN.add(pos.toImmutable()); //Convert to immutable because chunk delta updates use the mutable variant
+ } else if (BUTTONS_AREA.contains(posVec) && block.equals(Blocks.AIR)) {
+ //Upon reaching the showing of the next sequence we need to reset the state so that we don't show old data
+ //Otherwise, the nextIndex will go beyond 5 and that can cause bugs, it also helps with the other case noted above
+ reset();
+ }
+ }
+ }
+
+ private static void render(WorldRenderContext context) {
+ if (shouldProcess()) {
+ int buttonsRendered = 0;
+
+ for (BlockPos pos : SIMON_PATTERN) {
+ //Offset to west (x - 1) to get the position of the button from the sea lantern block
+ BlockPos buttonPos = pos.west();
+ ClientWorld world = Objects.requireNonNull(MinecraftClient.getInstance().world); //Should never be null here
+ BlockState state = world.getBlockState(buttonPos);
+
+ //If the button hasn't been clicked yet
+ //Also don't do anything if the button isn't there which means the device is showing the sequence
+ if (!CLICKED_BUTTONS.contains(buttonPos) && state.getBlock().equals(Blocks.STONE_BUTTON)) {
+ Box outline = RenderHelper.getBlockBoundingBox(world, state, buttonPos);
+ float[] colour = buttonsRendered == 0 ? GREEN : YELLOW;
+
+ RenderHelper.renderFilled(context, Boxes.getMinVec(outline), Boxes.getLengthVec(outline), colour, 0.5f, true);
+ RenderHelper.renderOutline(context, outline, colour, 5f, true);
+
+ if (++buttonsRendered == 2) return;
+ }
+ }
+ }
+ }
+
+ private static boolean shouldProcess() {
+ return SkyblockerConfigManager.get().dungeons.devices.solveSimonSays &&
+ Utils.isInDungeons() && DungeonManager.isInBoss() && DungeonManager.getBoss() == DungeonBoss.MAXOR;
+ }
+
+ private static void reset() {
+ CLICKED_BUTTONS.clear();
+ SIMON_PATTERN.clear();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
index b9986731..11f31f34 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
@@ -12,14 +12,16 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.serialization.JsonOps;
import de.hysky.skyblocker.SkyblockerMod;
-import de.hysky.skyblocker.config.configs.DungeonsConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.config.configs.DungeonsConfig;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.skyblock.dungeon.DungeonBoss;
import de.hysky.skyblocker.skyblock.dungeon.DungeonMap;
import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.Tickable;
import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientPosArgument;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.objects.Object2ByteMap;
import it.unimi.dsi.fastutil.objects.Object2ByteMaps;
@@ -37,8 +39,6 @@ import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.command.CommandSource;
-import net.minecraft.command.argument.BlockPosArgumentType;
-import net.minecraft.command.argument.PosArgument;
import net.minecraft.command.argument.TextArgumentType;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.entity.Entity;
@@ -53,7 +53,6 @@ import net.minecraft.item.Items;
import net.minecraft.item.map.MapState;
import net.minecraft.registry.Registry;
import net.minecraft.resource.Resource;
-import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Identifier;
@@ -387,13 +386,12 @@ public class DungeonManager {
return Command.SINGLE_SUCCESS;
}
- private static RequiredArgumentBuilder<FabricClientCommandSource, PosArgument> addCustomWaypointCommand(boolean relative, CommandRegistryAccess registryAccess) {
- return argument("pos", BlockPosArgumentType.blockPos())
+ private static RequiredArgumentBuilder<FabricClientCommandSource, ClientPosArgument> addCustomWaypointCommand(boolean relative, CommandRegistryAccess registryAccess) {
+ return argument("pos", ClientBlockPosArgumentType.blockPos())
.then(argument("secretIndex", IntegerArgumentType.integer())
.then(argument("category", SecretWaypoint.Category.CategoryArgumentType.category())
.then(argument("name", TextArgumentType.text(registryAccess)).executes(context -> {
- // TODO Less hacky way with custom ClientBlockPosArgumentType
- BlockPos pos = context.getArgument("pos", PosArgument.class).toAbsoluteBlockPos(new ServerCommandSource(null, context.getSource().getPosition(), context.getSource().getRotation(), null, 0, null, null, null, null));
+ BlockPos pos = context.getArgument("pos", ClientPosArgument.class).toAbsoluteBlockPos(context.getSource());
return relative ? addCustomWaypointRelative(context, pos) : addCustomWaypoint(context, pos);
}))
)
@@ -419,11 +417,10 @@ public class DungeonManager {
return Command.SINGLE_SUCCESS;
}
- private static RequiredArgumentBuilder<FabricClientCommandSource, PosArgument> removeCustomWaypointCommand(boolean relative) {
- return argument("pos", BlockPosArgumentType.blockPos())
+ private static RequiredArgumentBuilder<FabricClientCommandSource, ClientPosArgument> removeCustomWaypointCommand(boolean relative) {
+ return argument("pos", ClientBlockPosArgumentType.blockPos())
.executes(context -> {
- // TODO Less hacky way with custom ClientBlockPosArgumentType
- BlockPos pos = context.getArgument("pos", PosArgument.class).toAbsoluteBlockPos(new ServerCommandSource(null, context.getSource().getPosition(), context.getSource().getRotation(), null, 0, null, null, null, null));
+ BlockPos pos = context.getArgument("pos", ClientPosArgument.class).toAbsoluteBlockPos(context.getSource());
return relative ? removeCustomWaypointRelative(context, pos) : removeCustomWaypoint(context, pos);
});
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java
index d709181f..83167c18 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/CrystalsLocationsManager.java
@@ -8,6 +8,8 @@ import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientPosArgument;
import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -18,9 +20,6 @@ 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.command.argument.BlockPosArgumentType;
-import net.minecraft.command.argument.PosArgument;
-import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.ClickEvent;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
@@ -123,10 +122,10 @@ public class CrystalsLocationsManager {
private static void registerWaypointLocationCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) {
dispatcher.register(literal(SkyblockerMod.NAMESPACE)
.then(literal("crystalWaypoints")
- .then(argument("pos", BlockPosArgumentType.blockPos())
+ .then(argument("pos", ClientBlockPosArgumentType.blockPos())
.then(argument("place", StringArgumentType.greedyString())
.suggests((context, builder) -> suggestMatching(WAYPOINT_LOCATIONS.keySet(), builder))
- .executes(context -> addWaypointFromCommand(context.getSource(), getString(context, "place"), context.getArgument("pos", PosArgument.class)))
+ .executes(context -> addWaypointFromCommand(context.getSource(), getString(context, "place"), context.getArgument("pos", ClientPosArgument.class)))
)
)
.then(literal("share")
@@ -160,9 +159,8 @@ public class CrystalsLocationsManager {
return text;
}
- public static int addWaypointFromCommand(FabricClientCommandSource source, String place, PosArgument location) {
- // TODO Less hacky way with custom ClientBlockPosArgumentType
- BlockPos blockPos = location.toAbsoluteBlockPos(new ServerCommandSource(null, source.getPosition(), source.getRotation(), null, 0, null, null, null, null));
+ public static int addWaypointFromCommand(FabricClientCommandSource source, String place, ClientPosArgument location) {
+ BlockPos blockPos = location.toAbsoluteBlockPos(source);
if (WAYPOINT_LOCATIONS.containsKey(place)) {
addCustomWaypoint(place, blockPos);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java b/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java
index 025b3dce..2521b3a9 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/filters/SimpleChatFilter.java
@@ -2,11 +2,12 @@ package de.hysky.skyblocker.skyblock.filters;
import de.hysky.skyblocker.utils.chat.ChatPatternListener;
import net.minecraft.text.Text;
+import org.intellij.lang.annotations.Language;
import java.util.regex.Matcher;
public abstract class SimpleChatFilter extends ChatPatternListener {
- public SimpleChatFilter(String pattern) {
+ protected SimpleChatFilter(@Language("RegExp") String pattern) {
super(pattern);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java
index 76e7f02c..56ce7bf8 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorAnimatedDyes.java
@@ -1,20 +1,15 @@
package de.hysky.skyblocker.skyblock.item;
-import static com.mojang.brigadier.arguments.StringArgumentType.getString;
-import static com.mojang.brigadier.arguments.StringArgumentType.word;
-import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
-import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
-
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.BoolArgumentType;
import com.mojang.brigadier.arguments.IntegerArgumentType;
-
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.command.argumenttypes.color.ColorArgumentType;
import dev.isxander.yacl3.config.v2.api.SerialEntry;
import it.unimi.dsi.fastutil.objects.Object2ObjectFunction;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
@@ -27,6 +22,9 @@ import net.minecraft.registry.tag.ItemTags;
import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
public class CustomArmorAnimatedDyes {
private static final Object2ObjectOpenHashMap<AnimatedDye, AnimatedDyeStateTracker> STATE_TRACKER_MAP = new Object2ObjectOpenHashMap<>();
private static final Object2ObjectFunction<AnimatedDye, AnimatedDyeStateTracker> NEW_STATE_TRACKER = _dye -> AnimatedDyeStateTracker.create();
@@ -42,23 +40,17 @@ public class CustomArmorAnimatedDyes {
dispatcher.register(literal(SkyblockerMod.NAMESPACE)
.then(literal("custom")
.then(literal("animatedDye")
- .executes(context -> customizeAnimatedDye(context.getSource(), null, null, 0, false, 0))
- .then(argument("hex1", word())
- .then(argument("hex2", word())
+ .executes(context -> customizeAnimatedDye(context.getSource(), Integer.MIN_VALUE, Integer.MIN_VALUE, 0, false, 0))
+ .then(argument("hex1", ColorArgumentType.hex())
+ .then(argument("hex2", ColorArgumentType.hex())
.then(argument("samples", IntegerArgumentType.integer(1))
.then(argument("cycleBack", BoolArgumentType.bool())
- .executes(context -> customizeAnimatedDye(context.getSource(), getString(context, "hex1"), getString(context, "hex2"), IntegerArgumentType.getInteger(context, "samples"), BoolArgumentType.getBool(context, "cycleBack"), DEFAULT_TICK_DELAY))
+ .executes(context -> customizeAnimatedDye(context.getSource(), ColorArgumentType.getIntFromHex(context, "hex1"), ColorArgumentType.getIntFromHex(context, "hex2"), IntegerArgumentType.getInteger(context, "samples"), BoolArgumentType.getBool(context, "cycleBack"), DEFAULT_TICK_DELAY))
.then(argument("tickDelay", IntegerArgumentType.integer(0, 20))
- .executes(context ->customizeAnimatedDye(context.getSource(), getString(context, "hex1"), getString(context, "hex2"), IntegerArgumentType.getInteger(context, "samples"), BoolArgumentType.getBool(context, "cycleBack"), IntegerArgumentType.getInteger(context, "tickDelay")))))))))));
+ .executes(context ->customizeAnimatedDye(context.getSource(), ColorArgumentType.getIntFromHex(context, "hex1"), ColorArgumentType.getIntFromHex(context, "hex2"), IntegerArgumentType.getInteger(context, "samples"), BoolArgumentType.getBool(context, "cycleBack"), IntegerArgumentType.getInteger(context, "tickDelay")))))))))));
}
- private static int customizeAnimatedDye(FabricClientCommandSource source, String hex1, String hex2, int samples, boolean cycleBack, int tickDelay) {
- if (hex1 != null && hex2 != null && (!CustomArmorDyeColors.isHexadecimalColor(hex1) || !CustomArmorDyeColors.isHexadecimalColor(hex2))) {
- source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customAnimatedDyes.invalidHex")));
-
- return Command.SINGLE_SUCCESS;
- }
-
+ private static int customizeAnimatedDye(FabricClientCommandSource source, int color1, int color2, int samples, boolean cycleBack, int tickDelay) {
ItemStack heldItem = source.getPlayer().getMainHandStack();
if (Utils.isOnSkyblock() && heldItem != null && !heldItem.isEmpty()) {
@@ -68,7 +60,7 @@ public class CustomArmorAnimatedDyes {
if (!itemUuid.isEmpty()) {
Object2ObjectOpenHashMap<String, AnimatedDye> customAnimatedDyes = SkyblockerConfigManager.get().general.customAnimatedDyes;
- if (hex1 == null && hex2 == null) {
+ if (color1 == Integer.MIN_VALUE && color2 == Integer.MIN_VALUE) {
if (customAnimatedDyes.containsKey(itemUuid)) {
customAnimatedDyes.remove(itemUuid);
SkyblockerConfigManager.save();
@@ -77,7 +69,7 @@ public class CustomArmorAnimatedDyes {
source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customAnimatedDyes.neverHad")));
}
} else {
- AnimatedDye animatedDye = new AnimatedDye(Integer.decode("0x" + hex1.replace("#", "")), Integer.decode("0x" + hex2.replace("#", "")), samples, cycleBack, tickDelay);
+ AnimatedDye animatedDye = new AnimatedDye(color1, color2, samples, cycleBack, tickDelay);
customAnimatedDyes.put(itemUuid, animatedDye);
SkyblockerConfigManager.save();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java
index 97311220..62ffcf73 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java
@@ -2,11 +2,11 @@ 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.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.command.argumenttypes.color.ColorArgumentType;
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;
@@ -25,28 +25,23 @@ public class CustomArmorDyeColors {
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")))))));
+ .executes(context -> customizeDyeColor(context.getSource(), Integer.MIN_VALUE))
+ .then(ClientCommandManager.argument("hexCode", ColorArgumentType.hex())
+ .executes(context -> customizeDyeColor(context.getSource(), ColorArgumentType.getIntFromHex(context, "hexCode")))))));
}
@SuppressWarnings("SameReturnValue")
- private static int customizeDyeColor(FabricClientCommandSource source, String hex) {
+ private static int customizeDyeColor(FabricClientCommandSource source, int color) {
ItemStack heldItem = source.getPlayer().getMainHandStack();
- if (hex != null && !isHexadecimalColor(hex)) {
- source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.invalidHex")));
- return Command.SINGLE_SUCCESS;
- }
-
- if (Utils.isOnSkyblock() && heldItem != null) {
+ if (Utils.isOnSkyblock() && heldItem != null) {
if (heldItem.isIn(ItemTags.DYEABLE)) {
String itemUuid = ItemUtils.getItemUuid(heldItem);
if (!itemUuid.isEmpty()) {
Object2IntOpenHashMap<String> customDyeColors = SkyblockerConfigManager.get().general.customDyeColors;
- if (hex == null) {
+ if (color == Integer.MIN_VALUE) {
if (customDyeColors.containsKey(itemUuid)) {
customDyeColors.removeInt(itemUuid);
SkyblockerConfigManager.save();
@@ -55,7 +50,7 @@ public class CustomArmorDyeColors {
source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.neverHad")));
}
} else {
- customDyeColors.put(itemUuid, Integer.decode("0x" + hex.replace("#", "")).intValue());
+ customDyeColors.put(itemUuid, color);
SkyblockerConfigManager.save();
source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.added")));
}
@@ -72,8 +67,4 @@ public class CustomArmorDyeColors {
return Command.SINGLE_SUCCESS;
}
-
- public 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/slottext/SlotText.java b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotText.java
index 66c02ca1..73224509 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotText.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotText.java
@@ -1,7 +1,10 @@
package de.hysky.skyblocker.skyblock.item.slottext;
+import it.unimi.dsi.fastutil.objects.ObjectLists;
import net.minecraft.text.Text;
+import java.util.List;
+
public record SlotText(Text text, TextPosition position) {
public static SlotText bottomLeft(Text text) {
return new SlotText(text, TextPosition.BOTTOM_LEFT);
@@ -18,4 +21,20 @@ public record SlotText(Text text, TextPosition position) {
public static SlotText topRight(Text text) {
return new SlotText(text, TextPosition.TOP_RIGHT);
}
+
+ public static List<SlotText> topLeftList(Text text) {
+ return ObjectLists.singleton(topLeft(text));
+ }
+
+ public static List<SlotText> topRightList(Text text) {
+ return ObjectLists.singleton(topRight(text));
+ }
+
+ public static List<SlotText> bottomLeftList(Text text) {
+ return ObjectLists.singleton(bottomLeft(text));
+ }
+
+ public static List<SlotText> bottomRightList(Text text) {
+ return ObjectLists.singleton(bottomRight(text));
+ }
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotTextManager.java b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotTextManager.java
index d3941d77..aa9bf939 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotTextManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/SlotTextManager.java
@@ -1,5 +1,6 @@
package de.hysky.skyblocker.skyblock.item.slottext;
+import de.hysky.skyblocker.skyblock.bazaar.BazaarHelper;
import de.hysky.skyblocker.skyblock.item.slottext.adders.*;
import de.hysky.skyblocker.utils.Utils;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
@@ -30,6 +31,7 @@ public class SlotTextManager {
new CommunityShopAdder(),
new YourEssenceAdder(),
new PowerStonesGuideAdder(),
+ new BazaarHelper(),
new StatsTuningAdder()
};
private static final ArrayList<SlotTextAdder> currentScreenAdders = new ArrayList<>();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/CollectionAdder.java b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/CollectionAdder.java
index 207190c2..d6ced22a 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/CollectionAdder.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/CollectionAdder.java
@@ -2,6 +2,7 @@ package de.hysky.skyblocker.skyblock.item.slottext.adders;
import de.hysky.skyblocker.skyblock.item.slottext.SlotText;
import de.hysky.skyblocker.skyblock.item.slottext.SlotTextAdder;
+import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.RomanNumerals;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
@@ -25,7 +26,11 @@ public class CollectionAdder extends SlotTextAdder {
Matcher matcher = COLLECTION.matcher(stack.getName().getString());
if (matcher.matches()) {
int level = RomanNumerals.romanToDecimal(matcher.group("level"));
- return List.of(SlotText.bottomRight(Text.literal(String.valueOf(level)).withColor(0xFFDDC1)));
+ if (ItemUtils.getLoreLineIf(stack, s -> s.contains("Progress to ")) != null) {
+ return List.of(SlotText.bottomRight(Text.literal(String.valueOf(level)).withColor(0xFFDDC1)));
+ } else {
+ return List.of(SlotText.bottomRight(Text.literal(String.valueOf(level)).withColor(0xE5B80B)));
+ }
}
return List.of();
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/SkillLevelAdder.java b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/SkillLevelAdder.java
index 07b8dd9b..18dbd2f7 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/SkillLevelAdder.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/slottext/adders/SkillLevelAdder.java
@@ -2,7 +2,9 @@ package de.hysky.skyblocker.skyblock.item.slottext.adders;
import de.hysky.skyblocker.skyblock.item.slottext.SlotText;
import de.hysky.skyblocker.skyblock.item.slottext.SlotTextAdder;
+import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.RomanNumerals;
+import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
@@ -20,11 +22,16 @@ public class SkillLevelAdder extends SlotTextAdder {
switch (slot.id) {
case 19, 20, 21, 22, 23, 24, 25, 29, 30, 31, 32 -> { //These are the slots that contain the skill items. Note that they aren't continuous, as there are 2 rows.
String name = slot.getStack().getName().getString();
+ final ItemStack stack = slot.getStack();
int lastIndex = name.lastIndexOf(' ');
if (lastIndex == -1) return List.of(SlotText.bottomLeft(Text.literal("0").formatted(Formatting.LIGHT_PURPLE))); //Skills without any levels don't display any roman numerals. Probably because 0 doesn't exist.
String romanNumeral = name.substring(lastIndex + 1); //+1 because we don't need the space itself
//The "romanNumeral" might be a latin numeral, too. There's a skyblock setting for this, so we have to do it this way V
- return List.of(SlotText.bottomLeft(Text.literal(String.valueOf(RomanNumerals.isValidRomanNumeral(romanNumeral) ? RomanNumerals.romanToDecimal(romanNumeral) : Integer.parseInt(romanNumeral))).withColor(0xFFDDC1)));
+ if (ItemUtils.getLoreLineIf(stack, s -> s.contains("Max Skill level reached!")) != null) {
+ return List.of(SlotText.bottomLeft(Text.literal(String.valueOf(RomanNumerals.isValidRomanNumeral(romanNumeral) ? RomanNumerals.romanToDecimal(romanNumeral) : Integer.parseInt(romanNumeral))).withColor(0xE5B80B)));
+ } else {
+ return List.of(SlotText.bottomLeft(Text.literal(String.valueOf(RomanNumerals.isValidRomanNumeral(romanNumeral) ? RomanNumerals.romanToDecimal(romanNumeral) : Integer.parseInt(romanNumeral))).withColor(0xFFDDC1)));
+ }
}
default -> {
return List.of();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipAdder.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipAdder.java
index 9bd63adc..f3395def 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipAdder.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipAdder.java
@@ -4,6 +4,7 @@ import de.hysky.skyblocker.utils.render.gui.AbstractContainerMatcher;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot;
import net.minecraft.text.Text;
+import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -19,7 +20,7 @@ public abstract class TooltipAdder extends AbstractContainerMatcher {
*/
public final int priority;
- protected TooltipAdder(String titlePattern, int priority) {
+ protected TooltipAdder(@Language("RegExp") String titlePattern, int priority) {
super(titlePattern);
this.priority = priority;
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java
index cb8efb0c..bd06acba 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipManager.java
@@ -1,6 +1,7 @@
package de.hysky.skyblocker.skyblock.item.tooltip;
import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor;
+import de.hysky.skyblocker.skyblock.bazaar.ReorderHelper;
import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver;
import de.hysky.skyblocker.skyblock.item.tooltip.adders.*;
import de.hysky.skyblocker.skyblock.item.tooltip.adders.CraftPriceTooltip;
@@ -24,6 +25,7 @@ public class TooltipManager {
new LineSmoothener(), // Applies before anything else
new SupercraftReminder(),
new ChocolateFactorySolver.Tooltip(),
+ new ReorderHelper.Tooltip(),
new NpcPriceTooltip(1),
new BazaarPriceTooltip(2),
new LBinTooltip(3),
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java
index 01ffc144..ab61b684 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java
@@ -2,6 +2,7 @@ package de.hysky.skyblocker.skyblock.itemlist;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.NEURepoManager;
+import de.hysky.skyblocker.utils.TextTransformer;
import io.github.moulberry.repo.constants.PetNumbers;
import io.github.moulberry.repo.data.NEUItem;
import io.github.moulberry.repo.data.Rarity;
@@ -53,10 +54,10 @@ public class ItemStackBuilder {
// Item Name
String name = injectData(item.getDisplayName(), injectors);
- stack.set(DataComponentTypes.CUSTOM_NAME, Text.of(name));
+ stack.set(DataComponentTypes.CUSTOM_NAME, TextTransformer.fromLegacy(name));
// Lore
- stack.set(DataComponentTypes.LORE, new LoreComponent(item.getLore().stream().map(line -> Text.of(injectData(line, injectors))).toList()));
+ stack.set(DataComponentTypes.LORE, new LoreComponent(item.getLore().stream().map(line -> TextTransformer.fromLegacy(injectData(line, injectors))).map(Text.class::cast).toList()));
String nbttag = item.getNbttag();
// add skull texture
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java
index dfb53628..bb236526 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/SearchResultsWidget.java
@@ -73,6 +73,8 @@ public class SearchResultsWidget implements Drawable, Element {
}
protected void updateSearchResult(String searchText) {
+ searchText = searchText.toLowerCase(Locale.ENGLISH);
+
if (!searchText.equals(this.searchText)) {
this.searchText = searchText;
this.searchResults.clear();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java
index 306fe279..a9f83ca8 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/collections/GenericCategory.java
@@ -132,6 +132,8 @@ public class GenericCategory implements ProfileViewerPage {
String[] parts = tierText.split("/");
int cTier = Integer.parseInt(parts[0].trim());
Color colour = itemStack.hasGlint() ? Color.MAGENTA : Color.darkGray;
+ //DO NOT CHANGE THIS METHOD CALL! Aaron's Mod mixes in here to provide chroma text for max collections
+ //and changing the method called here will break that! Consult Aaron before making any changes :)
context.drawText(textRenderer, Text.literal(toRomanNumerals(cTier)), x + 9 - (textRenderer.getWidth(toRomanNumerals(cTier)) / 2), y + 21, colour.getRGB(), false);
}
break;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/itemLoaders/ItemLoader.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/itemLoaders/ItemLoader.java
index f3045c11..11280af1 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/itemLoaders/ItemLoader.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/itemLoaders/ItemLoader.java
@@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.profileviewer.inventory.itemLoaders;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.serialization.JsonOps;
+
import de.hysky.skyblocker.skyblock.PetCache;
import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
import de.hysky.skyblocker.skyblock.profileviewer.ProfileViewerScreen;
@@ -10,6 +11,7 @@ import de.hysky.skyblocker.skyblock.profileviewer.inventory.Pet;
import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.NEURepoManager;
+import de.hysky.skyblocker.utils.TextTransformer;
import io.github.moulberry.repo.data.NEUItem;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.*;
@@ -21,6 +23,7 @@ import net.minecraft.registry.Registries;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
+import net.minecraft.util.Util;
import java.io.ByteArrayInputStream;
import java.util.*;
@@ -79,13 +82,13 @@ public class ItemLoader {
// Item Name
- stack.set(DataComponentTypes.CUSTOM_NAME, Text.of(nbttag.getCompound("display").getString("Name")));
+ stack.set(DataComponentTypes.CUSTOM_NAME, TextTransformer.fromLegacy(nbttag.getCompound("display").getString("Name")));
// Lore
NbtList loreList = nbttag.getCompound("display").getList("Lore", 8);
stack.set(DataComponentTypes.LORE, new LoreComponent(loreList.stream()
.map(NbtElement::asString)
- .map(Text::literal)
+ .map(TextTransformer::fromLegacy)
.collect(Collectors.toList())));
// add skull texture
@@ -111,6 +114,9 @@ public class ItemLoader {
// Set Count
stack.setCount(containerContent.getCompound(i).getInt("Count"));
+ // Attach an override for Aaron's Mod so that these ItemStacks will work with the mod's features even when not in Skyblock
+ extraAttributes.put("aaron-mod", Util.make(new NbtCompound(), comp -> comp.putBoolean("alwaysDisplaySkyblockInfo", true)));
+
stack.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(extraAttributes));
itemList.add(stack);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java
index ffeba7ea..2b064770 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java
@@ -5,7 +5,10 @@ import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.ColorUtils;
import de.hysky.skyblocker.utils.ItemUtils;
+import de.hysky.skyblocker.utils.Location;
import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientPosArgument;
import de.hysky.skyblocker.utils.render.RenderHelper;
import de.hysky.skyblocker.utils.waypoint.Waypoint;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -19,7 +22,6 @@ import net.fabricmc.fabric.api.event.player.UseItemCallback;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.command.argument.BlockPosArgumentType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.s2c.play.ParticleS2CPacket;
@@ -67,8 +69,8 @@ public class MythologicalRitual {
return Command.SINGLE_SUCCESS;
}))
.then(literal("clearGriffinBurrow")
- .then(argument("pos", BlockPosArgumentType.blockPos()).executes(context -> {
- griffinBurrows.remove(context.getArgument("pos", BlockPos.class));
+ .then(argument("position", ClientBlockPosArgumentType.blockPos()).executes(context -> {
+ griffinBurrows.remove(context.getArgument("position", ClientPosArgument.class).toAbsoluteBlockPos(context.getSource()));
return Command.SINGLE_SUCCESS;
}))
)
@@ -190,7 +192,7 @@ public class MythologicalRitual {
}
private static boolean isActive() {
- return SkyblockerConfigManager.get().helpers.mythologicalRitual.enableMythologicalRitualHelper && Utils.getLocationRaw().equals("hub");
+ return SkyblockerConfigManager.get().helpers.mythologicalRitual.enableMythologicalRitualHelper && Utils.getLocation() == Location.HUB;
}
private static void reset() {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java
index 11ec1b8d..b88eb38f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/OrderedWaypoints.java
@@ -12,9 +12,12 @@ import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.skyblock.item.CustomArmorDyeColors;
+import de.hysky.skyblocker.utils.ColorUtils;
import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.Utils;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientBlockPosArgumentType;
+import de.hysky.skyblocker.utils.command.argumenttypes.blockpos.ClientPosArgument;
+import de.hysky.skyblocker.utils.command.argumenttypes.color.ColorArgumentType;
import de.hysky.skyblocker.utils.render.RenderHelper;
import de.hysky.skyblocker.utils.waypoint.Waypoint;
import it.unimi.dsi.fastutil.floats.FloatArrayList;
@@ -30,9 +33,6 @@ 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.BlockPosArgumentType;
-import net.minecraft.command.argument.PosArgument;
-import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
@@ -87,24 +87,24 @@ public class OrderedWaypoints {
.then(literal("add")
.then(argument("groupName", word())
.suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder))
- .then(argument("pos", BlockPosArgumentType.blockPos())
- .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), Integer.MIN_VALUE, null))
- .then(argument("hex", word())
- .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), Integer.MIN_VALUE, getString(context, "hex")))))))
+ .then(argument("pos", ClientBlockPosArgumentType.blockPos())
+ .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), Integer.MIN_VALUE, Integer.MIN_VALUE))
+ .then(argument("hex", ColorArgumentType.hex())
+ .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), Integer.MIN_VALUE, ColorArgumentType.getIntFromHex(context, "hex")))))))
.then(literal("addAt")
.then(argument("groupName", word())
.suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder))
.then(argument("index", IntegerArgumentType.integer(0))
- .then(argument("pos", BlockPosArgumentType.blockPos())
- .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), IntegerArgumentType.getInteger(context, "index"), null))
- .then(argument("hex", word())
- .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), IntegerArgumentType.getInteger(context, "index"), getString(context, "hex"))))))))
+ .then(argument("pos", ClientBlockPosArgumentType.blockPos())
+ .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), IntegerArgumentType.getInteger(context, "index"), Integer.MIN_VALUE))
+ .then(argument("hex", ColorArgumentType.hex())
+ .executes(context -> addWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), IntegerArgumentType.getInteger(context, "index"), ColorArgumentType.getIntFromHex(context, "hex"))))))))
.then(literal("remove")
.then(argument("groupName", word())
.suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder))
.executes(context -> removeWaypointGroup(context.getSource(), getString(context, "groupName")))
- .then(argument("pos", BlockPosArgumentType.blockPos())
- .executes(context -> removeWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", PosArgument.class), Integer.MIN_VALUE)))))
+ .then(argument("pos", ClientBlockPosArgumentType.blockPos())
+ .executes(context -> removeWaypoint(context.getSource(), getString(context, "groupName"), context.getArgument("pos", ClientPosArgument.class), Integer.MIN_VALUE)))))
.then(literal("removeAt")
.then(argument("groupName", word())
.suggests((source, builder) -> CommandSource.suggestMatching(WAYPOINTS.keySet(), builder))
@@ -126,20 +126,12 @@ public class OrderedWaypoints {
.executes(context -> export(context.getSource()))))));
}
- private static int addWaypoint(FabricClientCommandSource source, String groupName, PosArgument posArgument, int index, String hex) {
- BlockPos pos = posArgument.toAbsoluteBlockPos(new ServerCommandSource(null, source.getPosition(), source.getRotation(), null, 0, null, null, null, null));
+ private static int addWaypoint(FabricClientCommandSource source, String groupName, ClientPosArgument posArgument, int index, int color) {
+ BlockPos pos = posArgument.toAbsoluteBlockPos(source);
SEMAPHORE.acquireUninterruptibly();
- if (hex != null && !CustomArmorDyeColors.isHexadecimalColor(hex)) {
- source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.waypoints.ordered.add.invalidHexColor")));
- SEMAPHORE.release();
-
- return Command.SINGLE_SUCCESS;
- }
-
- int rgb = hex != null ? Integer.decode("0x" + hex.replace("#", "")) : Integer.MIN_VALUE;
- float[] colorComponents = rgb != Integer.MIN_VALUE ? new float[] { ((rgb >> 16) & 0xFF) / 255f, ((rgb >> 8) & 0xFF) / 255f, (rgb & 0xFF) / 255f } : new float[0];
+ float[] colorComponents = color != Integer.MIN_VALUE ? ColorUtils.getFloatComponents(color) : new float[0];
OrderedWaypointGroup group = WAYPOINTS.computeIfAbsent(groupName, name -> new OrderedWaypointGroup(name, true, new ObjectArrayList<>()));
OrderedWaypoint waypoint = new OrderedWaypoint(pos, colorComponents);
@@ -175,13 +167,13 @@ public class OrderedWaypoints {
return Command.SINGLE_SUCCESS;
}
- private static int removeWaypoint(FabricClientCommandSource source, String groupName, PosArgument posArgument, int index) {
+ private static int removeWaypoint(FabricClientCommandSource source, String groupName, ClientPosArgument posArgument, int index) {
if (WAYPOINTS.containsKey(groupName)) {
SEMAPHORE.acquireUninterruptibly();
OrderedWaypointGroup group = WAYPOINTS.get(groupName);
if (posArgument != null) {
- BlockPos pos = posArgument.toAbsoluteBlockPos(new ServerCommandSource(null, source.getPosition(), source.getRotation(), null, 0, null, null, null, null));
+ BlockPos pos = posArgument.toAbsoluteBlockPos(source);
group.waypoints().removeIf(waypoint -> waypoint.getPos().equals(pos));
INDEX_STORE.removeInt(group.name());
diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java
index e43ce783..65807886 100644
--- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java
@@ -43,7 +43,7 @@ import java.util.regex.Pattern;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
-public class ItemUtils {
+public final class ItemUtils {
public static final String ID = "id";
public static final String UUID = "uuid";
public static final Pattern NOT_DURABILITY = Pattern.compile("[^0-9 /]");
@@ -55,6 +55,8 @@ public class ItemUtils {
ComponentChanges.CODEC.optionalFieldOf("components", ComponentChanges.EMPTY).forGetter(ItemStack::getComponentChanges)
).apply(instance, ItemStack::new)));
+ private ItemUtils() {}
+
public static LiteralArgumentBuilder<FabricClientCommandSource> dumpHeldItemCommand() {
return literal("dumpHeldItem").executes(context -> {
context.getSource().sendFeedback(Text.literal("[Skyblocker Debug] Held Item: " + SkyblockerMod.GSON_COMPACT.toJson(ItemStack.CODEC.encodeStart(JsonOps.INSTANCE, context.getSource().getPlayer().getMainHandStack()).getOrThrow())));
@@ -198,9 +200,13 @@ public class ItemUtils {
return null;
}
+ /**
+ * Gets the first line of the lore that matches the specified predicate.
+ * @return The first line of the lore that matches the predicate, or {@code null} if no line matches.
+ */
@Nullable
- public static String getLoreLineIf(ItemStack item, Predicate<String> predicate) {
- for (Text line : getLore(item)) {
+ public static String getLoreLineIf(ItemStack stack, Predicate<String> predicate) {
+ for (Text line : getLore(stack)) {
String string = line.getString();
if (predicate.test(string)) {
return string;
@@ -210,21 +216,40 @@ public class ItemUtils {
return null;
}
+ /**
+ * Gets the first line of the lore that matches the specified pattern, using {@link Matcher#matches()}.
+ * @return A matcher that contains match results if the pattern was found in the lore, otherwise {@code null}.
+ */
@Nullable
- public static Matcher getLoreLineIfMatch(ItemStack item, Pattern pattern) {
- for (Text line : getLore(item)) {
- String string = line.getString();
- Matcher matcher = pattern.matcher(string);
- if (matcher.matches()) {
+ public static Matcher getLoreLineIfMatch(ItemStack stack, Pattern pattern) {
+ Matcher matcher = pattern.matcher("");
+ for (Text line : getLore(stack)) {
+ if (matcher.reset(line.getString()).matches()) {
return matcher;
}
}
-
return null;
}
- public static @NotNull List<Text> getLore(ItemStack item) {
- return item.getOrDefault(DataComponentTypes.LORE, LoreComponent.DEFAULT).styledLines();
+ /**
+ * Gets the first line of the lore that matches the specified pattern, using {@link Matcher#find()}.
+ * @param pattern the pattern to search for
+ * @param stack the stack to search the lore of
+ * @return A {@link Matcher matcher} that contains match results if the pattern was found in the lore, otherwise {@code null}.
+ */
+ @Nullable
+ public static Matcher getLoreLineIfContainsMatch(ItemStack stack, Pattern pattern) {
+ Matcher matcher = pattern.matcher("");
+ for (Text line : getLore(stack)) {
+ if (matcher.reset(line.getString()).find()) {
+ return matcher;
+ }
+ }
+ return null;
+ }
+
+ public static @NotNull List<Text> getLore(ItemStack stack) {
+ return stack.getOrDefault(DataComponentTypes.LORE, LoreComponent.DEFAULT).styledLines();
}
public static @NotNull PropertyMap propertyMapWithTexture(String textureValue) {
diff --git a/src/main/java/de/hysky/skyblocker/utils/TextTransformer.java b/src/main/java/de/hysky/skyblocker/utils/TextTransformer.java
new file mode 100644
index 00000000..b8fb5101
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/TextTransformer.java
@@ -0,0 +1,92 @@
+package de.hysky.skyblocker.utils;
+
+import org.jetbrains.annotations.NotNull;
+
+import it.unimi.dsi.fastutil.chars.CharList;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Style;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+/**
+ * Contains utilities for transforming text. These methods are from Aaron's Mod.
+ *
+ * @author AzureAaron
+ */
+public class TextTransformer {
+ private static final CharList FORMAT_CODES = CharList.of('4', 'c', '6', 'e', '2', 'a','b', '3', '1', '9', 'd', '5', 'f', '7', '8', '0', 'r', 'k', 'l', 'm', 'n', 'o');
+
+ /**
+ * Converts strings with section symbol/legacy formatting to MutableText objects.
+ *
+ * @param legacy The string with legacy formatting to be transformed
+ * @return A {@link MutableText} object matching the exact formatting of the input
+ *
+ * @author AzureAaron
+ */
+ public static MutableText fromLegacy(@NotNull String legacy) {
+ MutableText newText = Text.empty();
+ StringBuilder builder = new StringBuilder();
+ Formatting formatting = null;
+ boolean bold = false;
+ boolean italic = false;
+ boolean underline = false;
+ boolean strikethrough = false;
+ boolean obfuscated = false;
+
+ for (int i = 0; i < legacy.length(); i++) {
+ //If we've encountered a new formatting code then append the text from the previous "sequence" and reset state
+ if (i != 0 && legacy.charAt(i - 1) == '§' && FORMAT_CODES.contains(Character.toLowerCase(legacy.charAt(i))) && !builder.isEmpty()) {
+ newText.append(Text.literal(builder.toString()).setStyle(Style.EMPTY
+ .withColor(formatting)
+ .withBold(bold)
+ .withItalic(italic)
+ .withUnderline(underline)
+ .withStrikethrough(strikethrough)
+ .withObfuscated(obfuscated)));
+
+ //Erase all characters in the builder so we can reuse it, also clear formatting
+ builder.delete(0, builder.length());
+ formatting = null;
+ bold = false;
+ italic = false;
+ underline = false;
+ strikethrough = false;
+ obfuscated = false;
+ }
+
+ if (i != 0 && legacy.charAt(i - 1) == '§') {
+ Formatting fmt = Formatting.byCode(legacy.charAt(i));
+
+ switch (fmt) {
+ case BOLD -> bold = true;
+ case ITALIC -> italic = true;
+ case UNDERLINE -> underline = true;
+ case STRIKETHROUGH -> strikethrough = true;
+ case OBFUSCATED -> obfuscated = true;
+
+ default -> formatting = fmt;
+ }
+
+ continue;
+ }
+
+ //This character isn't the start of a formatting sequence or this character isn't part of a formatting sequence
+ if (legacy.charAt(i) != '§' && (i == 0 || (i != 0 && legacy.charAt(i - 1) != '§'))) {
+ builder.append(legacy.charAt(i));
+ }
+
+ // We've read the last character so append the last text with all of the formatting
+ if (i == legacy.length() - 1) {
+ newText.append(Text.literal(builder.toString()).setStyle(Style.EMPTY
+ .withColor(formatting)
+ .withBold(bold)
+ .withItalic(italic)
+ .withUnderline(underline)
+ .withStrikethrough(strikethrough)
+ .withObfuscated(obfuscated)));
+ }
+ }
+ return newText;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java
index 708af280..e701e24c 100644
--- a/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java
+++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatPatternListener.java
@@ -1,6 +1,7 @@
package de.hysky.skyblocker.utils.chat;
import net.minecraft.text.Text;
+import org.intellij.lang.annotations.Language;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -9,7 +10,7 @@ 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) {
+ protected ChatPatternListener(@Language("RegExp") String pattern) {
this.pattern = Pattern.compile(pattern);
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/EggTypeArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/EggTypeArgumentType.java
new file mode 100644
index 00000000..a6532ff8
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/EggTypeArgumentType.java
@@ -0,0 +1,40 @@
+package de.hysky.skyblocker.utils.command.argumenttypes;
+
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.arguments.ArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.suggestion.Suggestions;
+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
+import de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder;
+import net.minecraft.command.CommandSource;
+
+import java.util.Collection;
+import java.util.concurrent.CompletableFuture;
+
+public final class EggTypeArgumentType implements ArgumentType<EggFinder.EggType> {
+ @Override
+ public EggFinder.EggType parse(StringReader reader) throws CommandSyntaxException {
+ String name = reader.readUnquotedString();
+ for (EggFinder.EggType type : EggFinder.EggType.entries) {
+ if (type.name().equalsIgnoreCase(name)) return type;
+ }
+ throw CommandSyntaxException.BUILT_IN_EXCEPTIONS.dispatcherUnknownArgument().create();
+ }
+
+ @Override
+ public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
+ return context.getSource() instanceof CommandSource
+ ? CommandSource.suggestMatching(EggFinder.EggType.entries.stream().map(EggFinder.EggType::name).map(String::toLowerCase), builder)
+ : Suggestions.empty();
+ }
+
+ @Override
+ public Collection<String> getExamples() {
+ return EggFinder.EggType.entries.stream().map(EggFinder.EggType::name).map(String::toLowerCase).toList();
+ }
+
+ public static EggTypeArgumentType eggType() {
+ return new EggTypeArgumentType();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientBlockPosArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientBlockPosArgumentType.java
new file mode 100644
index 00000000..3a226414
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientBlockPosArgumentType.java
@@ -0,0 +1,72 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.blockpos;
+
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.arguments.ArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.suggestion.Suggestions;
+import com.mojang.brigadier.suggestion.SuggestionsBuilder;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.command.CommandSource;
+import net.minecraft.command.argument.BlockPosArgumentType;
+import net.minecraft.server.command.CommandManager;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+
+import static net.minecraft.command.argument.BlockPosArgumentType.*;
+
+// Uses the static fields of BlockPosArgumentType to not create the same field twice
+public class ClientBlockPosArgumentType implements ArgumentType<ClientPosArgument> {
+ public static ClientBlockPosArgumentType blockPos() {
+ return new ClientBlockPosArgumentType();
+ }
+
+ public static BlockPos getLoadedBlockPos(CommandContext<FabricClientCommandSource> context, String name) throws CommandSyntaxException {
+ return getLoadedBlockPos(context, context.getSource().getWorld(), name);
+ }
+
+ public static BlockPos getLoadedBlockPos(CommandContext<FabricClientCommandSource> context, ClientWorld world, String name) throws CommandSyntaxException {
+ BlockPos blockPos = getBlockPos(context, name);
+ if (!world.isChunkLoaded(blockPos)) throw UNLOADED_EXCEPTION.create();
+ if (!world.isInBuildLimit(blockPos)) throw OUT_OF_WORLD_EXCEPTION.create();
+
+ return blockPos;
+ }
+
+ public static BlockPos getBlockPos(CommandContext<FabricClientCommandSource> context, String name) {
+ return context.getArgument(name, ClientPosArgument.class).toAbsoluteBlockPos(context.getSource());
+ }
+
+ public static BlockPos getValidBlockPos(CommandContext<FabricClientCommandSource> context, String name) throws CommandSyntaxException {
+ BlockPos blockPos = getBlockPos(context, name);
+ if (!World.isValid(blockPos)) {
+ throw OUT_OF_BOUNDS_EXCEPTION.create();
+ } else {
+ return blockPos;
+ }
+ }
+
+ public ClientPosArgument parse(StringReader stringReader) throws CommandSyntaxException {
+ return stringReader.canRead() && stringReader.peek() == '^' ? LookingClientPosArgument.parse(stringReader) : DefaultClientPosArgument.parse(stringReader);
+ }
+
+ @Override
+ public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
+ if (!(context.getSource() instanceof CommandSource commandSource)) return Suggestions.empty();
+
+ String string = builder.getRemaining();
+ Collection<CommandSource.RelativePosition> collection = !string.isEmpty() && string.charAt(0) == '^' ? Collections.singleton(CommandSource.RelativePosition.ZERO_LOCAL) : commandSource.getBlockPositionSuggestions();
+
+ return CommandSource.suggestPositions(string, collection, builder, CommandManager.getCommandValidator(this::parse));
+ }
+
+ @Override
+ public Collection<String> getExamples() {
+ return BlockPosArgumentType.EXAMPLES;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientPosArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientPosArgument.java
new file mode 100644
index 00000000..3dff86ab
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/ClientPosArgument.java
@@ -0,0 +1,27 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.blockpos;
+
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec2f;
+import net.minecraft.util.math.Vec3d;
+
+/**
+ * This interface, its 2 implementations and ClientBlockPosArgumentType are all copied from minecraft
+ * and converted to use FabricClientCommandSource instead of ServerCommandSource.
+ * This removes the need for hacky workarounds such as creating new ServerCommandSources with null or 0 on every argument.
+ */
+public interface ClientPosArgument {
+ Vec3d toAbsolutePos(FabricClientCommandSource source);
+
+ Vec2f toAbsoluteRotation(FabricClientCommandSource source);
+
+ default BlockPos toAbsoluteBlockPos(FabricClientCommandSource source) {
+ return BlockPos.ofFloored(this.toAbsolutePos(source));
+ }
+
+ boolean isXRelative();
+
+ boolean isYRelative();
+
+ boolean isZRelative();
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/DefaultClientPosArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/DefaultClientPosArgument.java
new file mode 100644
index 00000000..47258b9f
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/DefaultClientPosArgument.java
@@ -0,0 +1,113 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.blockpos;
+
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.command.argument.CoordinateArgument;
+import net.minecraft.command.argument.Vec3ArgumentType;
+import net.minecraft.util.math.Vec2f;
+import net.minecraft.util.math.Vec3d;
+
+public class DefaultClientPosArgument implements ClientPosArgument {
+ private final CoordinateArgument x;
+ private final CoordinateArgument y;
+ private final CoordinateArgument z;
+
+ public DefaultClientPosArgument(CoordinateArgument x, CoordinateArgument y, CoordinateArgument z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public Vec3d toAbsolutePos(FabricClientCommandSource source) {
+ Vec3d vec3d = source.getPosition();
+ return new Vec3d(this.x.toAbsoluteCoordinate(vec3d.x), this.y.toAbsoluteCoordinate(vec3d.y), this.z.toAbsoluteCoordinate(vec3d.z));
+ }
+
+ @Override
+ public Vec2f toAbsoluteRotation(FabricClientCommandSource source) {
+ Vec2f vec2f = source.getRotation();
+ return new Vec2f((float)this.x.toAbsoluteCoordinate(vec2f.x), (float)this.y.toAbsoluteCoordinate(vec2f.y));
+ }
+
+ @Override
+ public boolean isXRelative() {
+ return this.x.isRelative();
+ }
+
+ @Override
+ public boolean isYRelative() {
+ return this.y.isRelative();
+ }
+
+ @Override
+ public boolean isZRelative() {
+ return this.z.isRelative();
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ return o instanceof DefaultClientPosArgument defaultPosArgument &&
+ this.x.equals(defaultPosArgument.x) && this.y.equals(defaultPosArgument.y) && this.z.equals(defaultPosArgument.z);
+ }
+
+ public static DefaultClientPosArgument parse(StringReader reader) throws CommandSyntaxException {
+ int i = reader.getCursor();
+ CoordinateArgument coordinateArgument = CoordinateArgument.parse(reader);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ CoordinateArgument coordinateArgument2 = CoordinateArgument.parse(reader);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ CoordinateArgument coordinateArgument3 = CoordinateArgument.parse(reader);
+ return new DefaultClientPosArgument(coordinateArgument, coordinateArgument2, coordinateArgument3);
+ } else {
+ reader.setCursor(i);
+ throw Vec3ArgumentType.INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ } else {
+ reader.setCursor(i);
+ throw Vec3ArgumentType.INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ }
+
+ public static DefaultClientPosArgument parse(StringReader reader, boolean centerIntegers) throws CommandSyntaxException {
+ int i = reader.getCursor();
+ CoordinateArgument coordinateArgument = CoordinateArgument.parse(reader, centerIntegers);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ CoordinateArgument coordinateArgument2 = CoordinateArgument.parse(reader, false);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ CoordinateArgument coordinateArgument3 = CoordinateArgument.parse(reader, centerIntegers);
+ return new DefaultClientPosArgument(coordinateArgument, coordinateArgument2, coordinateArgument3);
+ } else {
+ reader.setCursor(i);
+ throw Vec3ArgumentType.INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ } else {
+ reader.setCursor(i);
+ throw Vec3ArgumentType.INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ }
+
+ public static DefaultClientPosArgument absolute(double x, double y, double z) {
+ return new DefaultClientPosArgument(new CoordinateArgument(false, x), new CoordinateArgument(false, y), new CoordinateArgument(false, z));
+ }
+
+ public static DefaultClientPosArgument absolute(Vec2f vec) {
+ return new DefaultClientPosArgument(new CoordinateArgument(false, vec.x), new CoordinateArgument(false, vec.y), new CoordinateArgument(true, 0.0));
+ }
+
+ public static DefaultClientPosArgument zero() {
+ return new DefaultClientPosArgument(new CoordinateArgument(true, 0.0), new CoordinateArgument(true, 0.0), new CoordinateArgument(true, 0.0));
+ }
+
+ public int hashCode() {
+ int i = this.x.hashCode();
+ i = 31 * i + this.y.hashCode();
+ return 31 * i + this.z.hashCode();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/LookingClientPosArgument.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/LookingClientPosArgument.java
new file mode 100644
index 00000000..3f89f9c7
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/blockpos/LookingClientPosArgument.java
@@ -0,0 +1,106 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.blockpos;
+
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.command.argument.CoordinateArgument;
+import net.minecraft.command.argument.Vec3ArgumentType;
+import net.minecraft.util.math.MathHelper;
+import net.minecraft.util.math.Vec2f;
+import net.minecraft.util.math.Vec3d;
+
+import java.util.Objects;
+
+public class LookingClientPosArgument implements ClientPosArgument {
+ private final double x;
+ private final double y;
+ private final double z;
+
+ public LookingClientPosArgument(double x, double y, double z) {
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ }
+
+ @Override
+ public Vec3d toAbsolutePos(FabricClientCommandSource source) {
+ Vec2f vec2f = source.getRotation();
+ Vec3d vec3d = source.getPlayer().getPos();
+ float f = MathHelper.cos((vec2f.y + 90.0F) * (float) (Math.PI / 180.0));
+ float g = MathHelper.sin((vec2f.y + 90.0F) * (float) (Math.PI / 180.0));
+ float h = MathHelper.cos(-vec2f.x * (float) (Math.PI / 180.0));
+ float i = MathHelper.sin(-vec2f.x * (float) (Math.PI / 180.0));
+ float j = MathHelper.cos((-vec2f.x + 90.0F) * (float) (Math.PI / 180.0));
+ float k = MathHelper.sin((-vec2f.x + 90.0F) * (float) (Math.PI / 180.0));
+ Vec3d vec3d2 = new Vec3d(f * h, i, g * h);
+ Vec3d vec3d3 = new Vec3d(f * j, k, g * j);
+ Vec3d vec3d4 = vec3d2.crossProduct(vec3d3).multiply(-1.0);
+ double d = vec3d2.x * this.z + vec3d3.x * this.y + vec3d4.x * this.x;
+ double e = vec3d2.y * this.z + vec3d3.y * this.y + vec3d4.y * this.x;
+ double l = vec3d2.z * this.z + vec3d3.z * this.y + vec3d4.z * this.x;
+ return new Vec3d(vec3d.x + d, vec3d.y + e, vec3d.z + l);
+ }
+
+ @Override
+ public Vec2f toAbsoluteRotation(FabricClientCommandSource source) {
+ return Vec2f.ZERO;
+ }
+
+ @Override
+ public boolean isXRelative() {
+ return true;
+ }
+
+ @Override
+ public boolean isYRelative() {
+ return true;
+ }
+
+ @Override
+ public boolean isZRelative() {
+ return true;
+ }
+
+ public static LookingClientPosArgument parse(StringReader reader) throws CommandSyntaxException {
+ int i = reader.getCursor();
+ double d = readCoordinate(reader, i);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ double e = readCoordinate(reader, i);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ double f = readCoordinate(reader, i);
+ return new LookingClientPosArgument(d, e, f);
+ } else {
+ reader.setCursor(i);
+ throw Vec3ArgumentType.INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ } else {
+ reader.setCursor(i);
+ throw Vec3ArgumentType.INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ }
+
+ private static double readCoordinate(StringReader reader, int startingCursorPos) throws CommandSyntaxException {
+ if (!reader.canRead()) {
+ throw CoordinateArgument.MISSING_COORDINATE.createWithContext(reader);
+ } else if (reader.peek() != '^') {
+ reader.setCursor(startingCursorPos);
+ throw Vec3ArgumentType.MIXED_COORDINATE_EXCEPTION.createWithContext(reader);
+ } else {
+ reader.skip();
+ return reader.canRead() && reader.peek() != ' ' ? reader.readDouble() : 0.0;
+ }
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+
+ return o instanceof LookingClientPosArgument lookingPosArgument
+ && this.x == lookingPosArgument.x && this.y == lookingPosArgument.y && this.z == lookingPosArgument.z;
+ }
+
+ public int hashCode() {
+ return Objects.hash(x, y, z);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/ColorArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/ColorArgumentType.java
new file mode 100644
index 00000000..a6dc8510
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/ColorArgumentType.java
@@ -0,0 +1,28 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.color;
+
+import com.mojang.brigadier.context.CommandContext;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+
+/**
+ * Utility class that provides static methods for abstracting away the actual argument type classes.
+ */
+public final class ColorArgumentType {
+ private ColorArgumentType() {} // Prevent instantiation
+
+ public static RgbColorArgumentType rgb() {
+ return new RgbColorArgumentType();
+ }
+
+ public static HexColorArgumentType hex() {
+ return new HexColorArgumentType();
+ }
+
+ public static int getIntFromHex(CommandContext<FabricClientCommandSource> context, String name) {
+ return HexColorArgumentType.getInt(context, name);
+ }
+
+ public static int getIntFromRgb(CommandContext<FabricClientCommandSource> context, String name) {
+ return RgbColorArgumentType.getInt(context, name);
+ }
+}
+
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/HexColorArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/HexColorArgumentType.java
new file mode 100644
index 00000000..516eb7b9
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/HexColorArgumentType.java
@@ -0,0 +1,38 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.color;
+
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.arguments.ArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.text.Text;
+import org.apache.commons.lang3.StringUtils;
+
+@SuppressWarnings("RedundantCast")
+public final class HexColorArgumentType implements ArgumentType<Integer> {
+ public static final DynamicCommandExceptionType WRONG_INPUT_WIDTH = new DynamicCommandExceptionType(found -> Text.translatable("argument.color.hex.invalidString", ((String) found).length()));
+ public static final DynamicCommandExceptionType INVALID_CHARACTER = new DynamicCommandExceptionType(character -> Text.translatable("argument.color.hex.invalidChar", (String) character));
+
+ @Override
+ public Integer parse(StringReader reader) throws CommandSyntaxException {
+ String input = reader.readString();
+ if (StringUtils.startsWithIgnoreCase(input, "0x")) input = input.substring(2);
+// else if (input.startsWith("#")) input = input.substring(1); // This doesn't work because minecraft has the # prefix reserved for tags, so inputs with that prefix never reach this reader
+
+ if (input.length() != 6) throw WRONG_INPUT_WIDTH.create(input);
+
+ for (int i = 0; i < input.length(); i++) {
+ char character = input.charAt(i);
+ if ((character < '0' || character > '9') && (character < 'a' || character > 'f') && (character < 'A' || character > 'F')) {
+ throw INVALID_CHARACTER.create(String.valueOf(character)); //Have to wrap character in a string, because mcdev doesn't appreciate chars and I cba to suppress the warnings
+ }
+ }
+
+ return Integer.decode("#" + input);
+ }
+
+ public static int getInt(CommandContext<FabricClientCommandSource> context, String name) {
+ return context.getArgument(name, Integer.class);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/RgbColorArgumentType.java b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/RgbColorArgumentType.java
new file mode 100644
index 00000000..d9a99fae
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/command/argumenttypes/color/RgbColorArgumentType.java
@@ -0,0 +1,39 @@
+package de.hysky.skyblocker.utils.command.argumenttypes.color;
+
+import com.mojang.brigadier.StringReader;
+import com.mojang.brigadier.arguments.ArgumentType;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.brigadier.exceptions.CommandSyntaxException;
+import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.text.Text;
+
+public final class RgbColorArgumentType implements ArgumentType<Integer> {
+ public static final SimpleCommandExceptionType INCOMPLETE_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.color.rgb.incomplete"));
+
+ @Override
+ public Integer parse(StringReader reader) throws CommandSyntaxException {
+ int i = reader.getCursor();
+ int redArgument = IntegerArgumentType.integer(0x00, 0xFF).parse(reader);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ int greenArgument = IntegerArgumentType.integer(0x00, 0xFF).parse(reader);
+ if (reader.canRead() && reader.peek() == ' ') {
+ reader.skip();
+ int blueArgument = IntegerArgumentType.integer(0x00, 0xFF).parse(reader);
+ return redArgument << 16 | greenArgument << 8 | blueArgument;
+ } else {
+ reader.setCursor(i);
+ throw INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ } else {
+ reader.setCursor(i);
+ throw INCOMPLETE_EXCEPTION.createWithContext(reader);
+ }
+ }
+
+ public static int getInt(CommandContext<FabricClientCommandSource> context, String name) {
+ return context.getArgument(name, Integer.class);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
index a5b9bf6b..1b16b138 100644
--- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
+++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
@@ -11,6 +11,7 @@ import de.hysky.skyblocker.utils.render.title.TitleContainer;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.Event;
+import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
import net.minecraft.client.gui.DrawContext;
@@ -20,6 +21,7 @@ import net.minecraft.client.texture.Scaling;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.BufferAllocator;
import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.client.world.ClientWorld;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.OrderedText;
import net.minecraft.text.Text;
@@ -64,18 +66,22 @@ public class RenderHelper {
}
public static void renderFilled(WorldRenderContext context, BlockPos pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) {
+ renderFilled(context, Vec3d.of(pos), dimensions, colorComponents, alpha, throughWalls);
+ }
+
+ public static void renderFilled(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) {
if (throughWalls) {
if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + dimensions.x, pos.getY() + dimensions.y, pos.getZ() + dimensions.z)) {
- renderFilled(context, Vec3d.of(pos), dimensions, colorComponents, alpha, true);
+ renderFilledInternal(context, pos, dimensions, colorComponents, alpha, true);
}
} else {
if (OcclusionCulling.getRegularCuller().isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + dimensions.x, pos.getY() + dimensions.y, pos.getZ() + dimensions.z)) {
- renderFilled(context, Vec3d.of(pos), dimensions, colorComponents, alpha, false);
+ renderFilledInternal(context, pos, dimensions, colorComponents, alpha, false);
}
}
}
- private static void renderFilled(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) {
+ private static void renderFilledInternal(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) {
MatrixStack matrices = context.matrixStack();
Vec3d camera = context.camera().getPos();
@@ -330,6 +336,14 @@ public class RenderHelper {
}
}
+ public static Box getBlockBoundingBox(ClientWorld world, BlockPos pos) {
+ return getBlockBoundingBox(world, world.getBlockState(pos), pos);
+ }
+
+ public static Box getBlockBoundingBox(ClientWorld world, BlockState state, BlockPos pos) {
+ return state.getOutlineShape(world, pos).asCuboid().getBoundingBox().offset(pos);
+ }
+
/**
* 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.
diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java
index 81c9ebec..9a9d0907 100644
--- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java
+++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolver.java
@@ -4,6 +4,7 @@ import de.hysky.skyblocker.SkyblockerMod;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.item.ItemStack;
+import org.intellij.lang.annotations.Language;
import java.util.List;
import java.util.regex.Pattern;
@@ -12,7 +13,7 @@ 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 extends AbstractContainerMatcher {
- protected ContainerSolver(String titlePattern) {
+ protected ContainerSolver(@Language("RegExp") String titlePattern) {
super(titlePattern);
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java
index 9a1e3072..79cc78f5 100644
--- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java
+++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java
@@ -1,10 +1,10 @@
package de.hysky.skyblocker.utils.render.gui;
import com.mojang.blaze3d.systems.RenderSystem;
-
import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor;
import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakeBagHelper;
import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakesHelper;
+import de.hysky.skyblocker.skyblock.bazaar.ReorderHelper;
import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver;
import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper;
import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit;
@@ -59,7 +59,8 @@ public class ContainerSolverManager {
UltrasequencerSolver.INSTANCE,
new NewYearCakeBagHelper(),
NewYearCakesHelper.INSTANCE,
- new ChocolateFactorySolver()
+ new ChocolateFactorySolver(),
+ new ReorderHelper()
};
}
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 6ea6cd70..ecdae5e0 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -1,4 +1,8 @@
{
+ "argument.color.rgb.incomplete": "Incomplete (expected 3 color values).",
+ "argument.color.hex.invalidString": "Hex integer must be exactly 6 characters long, found %d characters.",
+ "argument.color.hex.invalidChar": "Invalid character for hex integer: %s. Valid characters: [0-9a-fA-F]",
+
"key.categories.skyblocker": "Skyblocker",
"key.hotbarSlotLock": "Slot Lock (Hotbar)",
"key.skyblocker.toggleB": "Toggle tab HUD to screen B",
@@ -46,6 +50,12 @@
"skyblocker.config.dungeons.croesusHelper": "Croesus Helper",
"skyblocker.config.dungeons.croesusHelper.@Tooltip": "Gray out chests that have already been opened.",
+ "skyblocker.config.dungeons.devices": "Device Solvers (F7/M7)",
+ "skyblocker.config.dungeons.devices.solveLightsOn": "Solve Lights On",
+ "skyblocker.config.dungeons.devices.solveLightsOn.@Tooltip": "Highlights the correct levers to click in red",
+ "skyblocker.config.dungeons.devices.solveSimonSays": "Solve Simon Says",
+ "skyblocker.config.dungeons.devices.solveSimonSays.@Tooltip": "Highlights the correct button to click in green, and highlights the next one in yellow.",
+
"skyblocker.config.dungeons.doorHighlight": "Door Highlight",
"skyblocker.config.dungeons.doorHighlight.doorHighlightType": "Door Highlight Type",
"skyblocker.config.dungeons.doorHighlight.doorHighlightType.@Tooltip": "Highlight: Only displays a highlight.\n\nOutlined Highlight: Displays both a highlight and an outline.\n\nOutline: Only displays an outline.",
@@ -285,6 +295,10 @@
"skyblocker.config.helpers": "Helpers",
+ "skyblocker.config.helpers.bazaar": "Bazaar",
+ "skyblocker.config.helpers.bazaar.enableBazaarHelper": "Enable Bazaar Helper",
+ "skyblocker.config.helpers.bazaar.enableBazaarHelper.@Tooltip": "Draws icons on top of orders to explain the current state of the order.\n\n%s: Order is going to expire soon\n%s: Order has expired\n%s: Order is filled to some degree and there are items/coins to claim\n%s: Order is filled",
+
"skyblocker.config.helpers.chocolateFactory": "Chocolate Factory",
"skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper": "Enable Chocolate Factory Helper",
"skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper.@Tooltip": "Highlights the best upgrade when enabled. \n\nThe best upgrade is marked as green, but if you can't afford it, it's marked as yellow while marking the next best upgrade that you can afford as green.",
@@ -747,7 +761,6 @@
"skyblocker.events.tab.noMore": "No more this year!",
"skyblocker.events.tab.startsIn": "Starts in %s",
-
"skyblocker.garden.hud.mouseLocked": "Mouse locked.",
"skyblocker.fishing.reelNow": "Reel in now!",
@@ -896,6 +909,9 @@
"skyblocker.partyFinder.deList": "Click to de-list",
"skyblocker.partyFinder.join": "Click to join",
+ "skyblocker.reorderHelper.tooltip.line1": "[Skyblocker] You can copy the amount of items",
+ "skyblocker.reorderHelper.tooltip.line2": "by holding CTRL while clicking on the item!",
+
"skyblocker.fancyAuctionHouse.editBid": "Click to edit bid!",
"skyblocker.fancyAuctionHouse.price": "Price:",
"skyblocker.fancyAuctionHouse.newBid": "New Bid:",
diff --git a/src/main/resources/skyblocker.accesswidener b/src/main/resources/skyblocker.accesswidener
index 9fcdfa45..136124b7 100644
--- a/src/main/resources/skyblocker.accesswidener
+++ b/src/main/resources/skyblocker.accesswidener
@@ -18,3 +18,4 @@ accessible class net/minecraft/client/render/RenderPhase$LineWidth
accessible class net/minecraft/client/render/RenderPhase$ColorLogic
accessible class net/minecraft/client/render/RenderPhase$OffsetTexturing
accessible class net/minecraft/client/render/RenderPhase$Textures
+accessible field net/minecraft/command/argument/BlockPosArgumentType EXAMPLES Ljava/util/Collection;