aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de')
-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
44 files changed, 1188 insertions, 146 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()
};
}