aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java34
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java152
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java169
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java61
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java62
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java42
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java40
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java94
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java56
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java39
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java136
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java100
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java275
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java451
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java473
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java142
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java72
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java58
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java35
19 files changed, 0 insertions, 2491 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
deleted file mode 100644
index 67e6d7f4..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight;
-import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver;
-import net.minecraft.item.ItemStack;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class CroesusHelper extends ContainerSolver {
-
- public CroesusHelper() {
- super("^Croesus$");
- }
-
- @Override
- protected boolean isEnabled() {
- return SkyblockerConfigManager.get().locations.dungeons.croesusHelper;
- }
-
- @Override
- protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
- List<ColorHighlight> highlights = new ArrayList<>();
- for (Map.Entry<Integer, ItemStack> entry : slots.entrySet()) {
- ItemStack stack = entry.getValue();
- if (stack != null && stack.getNbt() != null && (stack.getNbt().toString().contains("No more Chests to open!") || stack.getNbt().toString().contains("Opened Chest:"))) {
- highlights.add(ColorHighlight.gray(entry.getKey()));
- }
- }
- return highlights;
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
deleted file mode 100644
index f39684eb..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import it.unimi.dsi.fastutil.objects.ObjectIntPair;
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
-import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.decoration.ArmorStandEntity;
-import net.minecraft.predicate.entity.EntityPredicates;
-import net.minecraft.util.math.Box;
-import net.minecraft.util.math.Vec3d;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * This class provides functionality to render outlines around Blaze entities
- */
-public class DungeonBlaze {
- private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName());
- private static final float[] GREEN_COLOR_COMPONENTS = {0.0F, 1.0F, 0.0F};
- private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f};
-
- private static ArmorStandEntity highestBlaze = null;
- private static ArmorStandEntity lowestBlaze = null;
- private static ArmorStandEntity nextHighestBlaze = null;
- private static ArmorStandEntity nextLowestBlaze = null;
-
- public static void init() {
- Scheduler.INSTANCE.scheduleCyclic(DungeonBlaze::update, 4);
- WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer);
- }
-
- /**
- * Updates the state of Blaze entities and triggers the rendering process if necessary.
- */
- public static void update() {
- ClientWorld world = MinecraftClient.getInstance().world;
- ClientPlayerEntity player = MinecraftClient.getInstance().player;
- if (world == null || player == null || !Utils.isInDungeons()) return;
- List<ObjectIntPair<ArmorStandEntity>> blazes = getBlazesInWorld(world, player);
- sortBlazes(blazes);
- updateBlazeEntities(blazes);
- }
-
- /**
- * Retrieves Blaze entities in the world and parses their health information.
- *
- * @param world The client world to search for Blaze entities.
- * @return A list of Blaze entities and their associated health.
- */
- private static List<ObjectIntPair<ArmorStandEntity>> getBlazesInWorld(ClientWorld world, ClientPlayerEntity player) {
- List<ObjectIntPair<ArmorStandEntity>> blazes = new ArrayList<>();
- for (ArmorStandEntity blaze : world.getEntitiesByClass(ArmorStandEntity.class, player.getBoundingBox().expand(500D), EntityPredicates.NOT_MOUNTED)) {
- String blazeName = blaze.getName().getString();
- if (blazeName.contains("Blaze") && blazeName.contains("/")) {
- try {
- int health = Integer.parseInt(blazeName.substring(blazeName.indexOf("/") + 1, blazeName.length() - 1));
- blazes.add(ObjectIntPair.of(blaze, health));
- } catch (NumberFormatException e) {
- handleException(e);
- }
- }
- }
- return blazes;
- }
-
- /**
- * Sorts the Blaze entities based on their health values.
- *
- * @param blazes The list of Blaze entities to be sorted.
- */
- private static void sortBlazes(List<ObjectIntPair<ArmorStandEntity>> blazes) {
- blazes.sort(Comparator.comparingInt(ObjectIntPair::rightInt));
- }
-
- /**
- * Updates information about Blaze entities based on sorted list.
- *
- * @param blazes The sorted list of Blaze entities with associated health values.
- */
- private static void updateBlazeEntities(List<ObjectIntPair<ArmorStandEntity>> blazes) {
- if (!blazes.isEmpty()) {
- lowestBlaze = blazes.get(0).left();
- int highestIndex = blazes.size() - 1;
- highestBlaze = blazes.get(highestIndex).left();
- if (blazes.size() > 1) {
- nextLowestBlaze = blazes.get(1).left();
- nextHighestBlaze = blazes.get(highestIndex - 1).left();
- }
- }
- }
-
- /**
- * Renders outlines for Blaze entities based on health and position.
- *
- * @param wrc The WorldRenderContext used for rendering.
- */
- public static void blazeRenderer(WorldRenderContext wrc) {
- try {
- if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfigManager.get().locations.dungeons.blazesolver) {
- if (highestBlaze.getY() < 69) {
- renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc);
- }
- if (lowestBlaze.getY() > 69) {
- renderBlazeOutline(lowestBlaze, nextLowestBlaze, wrc);
- }
- }
- } catch (Exception e) {
- handleException(e);
- }
- }
-
- /**
- * Renders outlines for Blaze entities and connections between them.
- *
- * @param blaze The Blaze entity for which to render an outline.
- * @param nextBlaze The next Blaze entity for connection rendering.
- * @param wrc The WorldRenderContext used for rendering.
- */
- private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) {
- Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(wrc, blazeBox, GREEN_COLOR_COMPONENTS, 5f);
-
- if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) {
- Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(wrc, nextBlazeBox, WHITE_COLOR_COMPONENTS, 5f);
-
- Vec3d blazeCenter = blazeBox.getCenter();
- Vec3d nextBlazeCenter = nextBlazeBox.getCenter();
-
- RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f);
- }
- }
-
- /**
- * Handles exceptions by logging and printing stack traces.
- *
- * @param e The exception to handle.
- */
- private static void handleException(Exception e) {
- LOGGER.warn("[Skyblocker BlazeRenderer] Encountered an unknown exception", e);
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java
deleted file mode 100644
index ea54ff32..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonChestProfit.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import com.google.gson.JsonObject;
-import it.unimi.dsi.fastutil.ints.IntBooleanPair;
-import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.mixin.accessor.ScreenAccessor;
-import me.xmrvizzy.skyblocker.skyblock.item.PriceInfoTooltip;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
-import net.minecraft.client.item.TooltipContext;
-import net.minecraft.item.ItemStack;
-import net.minecraft.screen.GenericContainerScreenHandler;
-import net.minecraft.screen.ScreenHandlerType;
-import net.minecraft.screen.slot.Slot;
-import net.minecraft.text.Text;
-import net.minecraft.util.Formatting;
-import org.apache.commons.lang3.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.text.DecimalFormat;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class DungeonChestProfit {
- private static final Logger LOGGER = LoggerFactory.getLogger(DungeonChestProfit.class);
- private static final Pattern ESSENCE_PATTERN = Pattern.compile("(?<type>[A-Za-z]+) Essence x(?<amount>[0-9]+)");
- private static final DecimalFormat FORMATTER = new DecimalFormat("#,###");
-
- public static void init() {
- ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> ScreenEvents.afterTick(screen).register(screen1 -> {
- if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen && genericContainerScreen.getScreenHandler().getType() == ScreenHandlerType.GENERIC_9X6) {
- ((ScreenAccessor) screen).setTitle(getChestProfit(genericContainerScreen.getScreenHandler(), screen.getTitle(), client));
- }
- }));
- }
-
- public static Text getChestProfit(GenericContainerScreenHandler handler, Text title, MinecraftClient client) {
- try {
- if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator && isDungeonChest(title.getString())) {
- int profit = 0;
- boolean hasIncompleteData = false, usedKismet = false;
- List<Slot> slots = handler.slots.subList(0, handler.getRows() * 9);
-
- //If the item stack for the "Open Reward Chest" button or the kismet button hasn't been sent to the client yet
- if (slots.get(31).getStack().isEmpty() || slots.get(50).getStack().isEmpty()) return title;
-
- for (Slot slot : slots) {
- ItemStack stack = slot.getStack();
-
- if (!stack.isEmpty()) {
- String name = stack.getName().getString();
- String id = PriceInfoTooltip.getInternalNameFromNBT(stack, false);
-
- //Regular item price
- if (id != null) {
- IntBooleanPair priceData = getItemPrice(id);
-
- if (!priceData.rightBoolean()) hasIncompleteData = true;
-
- //Add the item price to the profit
- profit += priceData.leftInt();
-
- continue;
- }
-
- //Essence price
- if (name.contains("Essence") && SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeEssence) {
- Matcher matcher = ESSENCE_PATTERN.matcher(name);
-
- if (matcher.matches()) {
- String type = matcher.group("type");
- int amount = Integer.parseInt(matcher.group("amount"));
-
- IntBooleanPair priceData = getItemPrice(("ESSENCE_" + type).toUpperCase());
-
- if (!priceData.rightBoolean()) hasIncompleteData = true;
-
- //Add the price of the essence to the profit
- profit += priceData.leftInt() * amount;
-
- continue;
- }
- }
-
- //Determine the cost of the chest
- if (name.contains("Open Reward Chest")) {
- String foundString = searchLoreFor(stack, client, "Coins");
-
- //Incase we're searching the free chest
- if (!StringUtils.isBlank(foundString)) {
- profit -= Integer.parseInt(foundString.replaceAll("[^0-9]", ""));
- }
-
- continue;
- }
-
- //Determine if a kismet was used or not
- if (name.contains("Reroll Chest")) {
- usedKismet = !StringUtils.isBlank(searchLoreFor(stack, client, "You already rerolled a chest!"));
- }
- }
- }
-
- if (SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.includeKismet && usedKismet) {
- IntBooleanPair kismetPriceData = getItemPrice("KISMET_FEATHER");
-
- if (!kismetPriceData.rightBoolean()) hasIncompleteData = true;
-
- profit -= kismetPriceData.leftInt();
- }
-
- return Text.literal(title.getString()).append(getProfitText(profit, hasIncompleteData));
- }
- } catch (Exception e) {
- LOGGER.error("[Skyblocker Profit Calculator] Failed to calculate dungeon chest profit! ", e);
- }
-
- return title;
- }
-
- /**
- * @return An {@link IntBooleanPair} with the {@code left int} representing the item's price, and the {@code right boolean} indicating if the price
- * was based on complete data.
- */
- private static IntBooleanPair getItemPrice(String id) {
- JsonObject bazaarPrices = PriceInfoTooltip.getBazaarPrices();
- JsonObject lbinPrices = PriceInfoTooltip.getLBINPrices();
-
- if (bazaarPrices == null || lbinPrices == null) return IntBooleanPair.of(0, false);
-
- if (bazaarPrices.has(id)) {
- JsonObject item = bazaarPrices.get(id).getAsJsonObject();
- boolean isPriceNull = item.get("sellPrice").isJsonNull();
-
- return IntBooleanPair.of(isPriceNull ? 0 : (int) item.get("sellPrice").getAsDouble(), !isPriceNull);
- }
-
- if (lbinPrices.has(id)) {
- return IntBooleanPair.of((int) lbinPrices.get(id).getAsDouble(), true);
- }
-
- return IntBooleanPair.of(0, false);
- }
-
- /**
- * Searches for a specific string of characters in the name and lore of an item
- */
- private static String searchLoreFor(ItemStack stack, MinecraftClient client, String searchString) {
- return stack.getTooltip(client.player, TooltipContext.BASIC).stream().map(Text::getString).filter(line -> line.contains(searchString)).findAny().orElse(null);
- }
-
- private static boolean isDungeonChest(String name) {
- return name.equals("Wood Chest") || name.equals("Gold Chest") || name.equals("Diamond Chest") || name.equals("Emerald Chest") || name.equals("Obsidian Chest") || name.equals("Bedrock Chest");
- }
-
- private static Text getProfitText(int profit, boolean hasIncompleteData) {
- SkyblockerConfig.DungeonChestProfit config = SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit;
- return getProfitText(profit, hasIncompleteData, config.neutralThreshold, config.neutralColor, config.profitColor, config.lossColor, config.incompleteColor);
- }
-
- static Text getProfitText(int profit, boolean hasIncompleteData, int neutralThreshold, Formatting neutralColor, Formatting profitColor, Formatting lossColor, Formatting incompleteColor) {
- return Text.literal((profit > 0 ? " +" : " ") + FORMATTER.format(profit)).formatted(hasIncompleteData ? incompleteColor : (Math.abs(profit) < neutralThreshold) ? neutralColor : (profit > 0) ? profitColor : lossColor);
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java
deleted file mode 100644
index 53cc4fac..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMap.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
-import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
-import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.gui.DrawContext;
-import net.minecraft.client.render.MapRenderer;
-import net.minecraft.client.render.VertexConsumerProvider;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.item.FilledMapItem;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.map.MapState;
-import net.minecraft.nbt.NbtCompound;
-import net.minecraft.util.Identifier;
-import org.apache.commons.lang3.StringUtils;
-
-public class DungeonMap {
- private static final Identifier MAP_BACKGROUND = new Identifier("textures/map/map_background.png");
-
- public static void render(MatrixStack matrices) {
- MinecraftClient client = MinecraftClient.getInstance();
- if (client.player == null || client.world == null) return;
- ItemStack item = client.player.getInventory().main.get(8);
- NbtCompound tag = item.getNbt();
-
- if (tag != null && tag.contains("map")) {
- String tag2 = tag.asString();
- tag2 = StringUtils.substringBetween(tag2, "map:", "}");
- int mapid = Integer.parseInt(tag2);
- VertexConsumerProvider.Immediate vertices = client.getBufferBuilders().getEffectVertexConsumers();
- MapRenderer map = client.gameRenderer.getMapRenderer();
- MapState state = FilledMapItem.getMapState(mapid, client.world);
- float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling;
- int x = SkyblockerConfigManager.get().locations.dungeons.mapX;
- int y = SkyblockerConfigManager.get().locations.dungeons.mapY;
-
- if (state == null) return;
- matrices.push();
- matrices.translate(x, y, 0);
- matrices.scale(scaling, scaling, 0f);
- map.draw(matrices, vertices, mapid, state, false, 15728880);
- vertices.draw();
- matrices.pop();
- }
- }
-
- public static void renderHUDMap(DrawContext context, int x, int y) {
- float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling;
- int size = (int) (128 * scaling);
- context.drawTexture(MAP_BACKGROUND, x, y, 0, 0, size, size, size, size);
- }
-
- public static void init() {
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("skyblocker")
- .then(ClientCommandManager.literal("hud")
- .then(ClientCommandManager.literal("dungeonmap")
- .executes(Scheduler.queueOpenScreenCommand(DungeonMapConfigScreen::new))))));
- }
-} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
deleted file mode 100644
index 94b05e86..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
-import net.minecraft.client.gui.DrawContext;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.text.Text;
-
-import java.awt.*;
-
-public class DungeonMapConfigScreen extends Screen {
-
- private int hudX = SkyblockerConfigManager.get().locations.dungeons.mapX;
- private int hudY = SkyblockerConfigManager.get().locations.dungeons.mapY;
- private final Screen parent;
-
- protected DungeonMapConfigScreen() {
- this(null);
- }
-
- public DungeonMapConfigScreen(Screen parent) {
- super(Text.literal("Dungeon Map Config"));
- this.parent = parent;
- }
-
- @Override
- public void render(DrawContext context, int mouseX, int mouseY, float delta) {
- super.render(context, mouseX, mouseY, delta);
- renderBackground(context, mouseX, mouseY, delta);
- DungeonMap.renderHUDMap(context, hudX, hudY);
- context.drawCenteredTextWithShadow(textRenderer, "Right Click To Reset Position", width >> 1, height >> 1, Color.GRAY.getRGB());
- }
-
- @Override
- public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
- float scaling = SkyblockerConfigManager.get().locations.dungeons.mapScaling;
- int size = (int) (128 * scaling);
- if (RenderHelper.pointIsInArea(mouseX, mouseY, hudX, hudY, hudX + size, hudY + size) && button == 0) {
- hudX = (int) Math.max(Math.min(mouseX - (size >> 1), this.width - size), 0);
- hudY = (int) Math.max(Math.min(mouseY - (size >> 1), this.height - size), 0);
- }
- return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
- }
-
- @Override
- public boolean mouseClicked(double mouseX, double mouseY, int button) {
- if (button == 1) {
- hudX = 2;
- hudY = 2;
- }
-
- return super.mouseClicked(mouseX, mouseY, button);
- }
-
- @Override
- public void close() {
- SkyblockerConfigManager.get().locations.dungeons.mapX = hudX;
- SkyblockerConfigManager.get().locations.dungeons.mapY = hudY;
- SkyblockerConfigManager.save();
- this.client.setScreen(parent);
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
deleted file mode 100644
index 2df8192d..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/LividColor.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler;
-import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.util.math.BlockPos;
-
-public class LividColor {
- private static int tenTicks = 0;
-
- public static void init() {
- ClientReceiveMessageEvents.GAME.register((message, overlay) -> {
- if (SkyblockerConfigManager.get().locations.dungeons.lividColor.enableLividColor && message.getString().equals("[BOSS] Livid: I respect you for making it to here, but I'll be your undoing.")) {
- tenTicks = 8;
- }
- });
- }
-
- public static void update() {
- MinecraftClient client = MinecraftClient.getInstance();
- if (tenTicks != 0) {
- if (SkyblockerConfigManager.get().locations.dungeons.lividColor.enableLividColor && Utils.isInDungeons() && client.world != null) {
- if (tenTicks == 1) {
- MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfigManager.get().locations.dungeons.lividColor.lividColorText.replace("[color]", "red"));
- tenTicks = 0;
- return;
- }
- String key = client.world.getBlockState(new BlockPos(5, 110, 42)).getBlock().getTranslationKey();
- if (key.startsWith("block.minecraft.") && key.endsWith("wool") && !key.endsWith("red_wool")) {
- MessageScheduler.INSTANCE.sendMessageAfterCooldown(SkyblockerConfigManager.get().locations.dungeons.lividColor.lividColorText.replace("[color]", key.substring(16, key.length() - 5)));
- tenTicks = 0;
- return;
- }
- tenTicks--;
- } else {
- tenTicks = 0;
- }
- }
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java
deleted file mode 100644
index b9fba7be..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/OldLever.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import net.minecraft.block.Block;
-import net.minecraft.block.enums.BlockFace;
-import net.minecraft.util.math.Direction;
-import net.minecraft.util.shape.VoxelShape;
-
-public class OldLever {
- protected static final VoxelShape FLOOR_SHAPE = Block.createCuboidShape(4.0D, 0.0D, 4.0D, 12.0D, 10.0D, 12.0D);
- protected static final VoxelShape NORTH_SHAPE = Block.createCuboidShape(5.0D, 3.0D, 10.0D, 11.0D, 13.0D, 16.0D);
- protected static final VoxelShape SOUTH_SHAPE = Block.createCuboidShape(5.0D, 3.0D, 0.0D, 11.0D, 13.0D, 6.0D);
- protected static final VoxelShape EAST_SHAPE = Block.createCuboidShape(0.0D, 3.0D, 5.0D, 6.0D, 13.0D, 11.0D);
- protected static final VoxelShape WEST_SHAPE = Block.createCuboidShape(10.0D, 3.0D, 5.0D, 16.0D, 13.0D, 11.0D);
-
- public static VoxelShape getShape(BlockFace face, Direction direction) {
- if (!SkyblockerConfigManager.get().general.hitbox.oldLeverHitbox)
- return null;
-
- if (face == BlockFace.FLOOR) {
- return FLOOR_SHAPE;
- } else if (face == BlockFace.WALL) {
- switch (direction) {
- case EAST -> {
- return EAST_SHAPE;
- }
- case WEST -> {
- return WEST_SHAPE;
- }
- case SOUTH -> {
- return SOUTH_SHAPE;
- }
- case NORTH -> {
- return NORTH_SHAPE;
- }
- }
- }
- return null;
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
deleted file mode 100644
index 288a8b5a..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Reparty.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
-import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener;
-import me.xmrvizzy.skyblocker.utils.scheduler.MessageScheduler;
-import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
-import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
-import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.text.Text;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Reparty extends ChatPatternListener {
- private static final MinecraftClient client = MinecraftClient.getInstance();
- public static final Pattern PLAYER = Pattern.compile(" ([a-zA-Z0-9_]{2,16}) ●");
- private static final int BASE_DELAY = 10;
-
- private String[] players;
- private int playersSoFar;
- private boolean repartying;
- private String partyLeader;
-
- public Reparty() {
- super("^(?:You are not currently in a party\\." +
- "|Party (?:Membe|Moderato)rs(?: \\(([0-9]+)\\)|:( .*))" +
- "|([\\[A-z+\\]]* )?(?<disband>.*) has disbanded .*" +
- "|.*\n([\\[A-z+\\]]* )?(?<invite>.*) has invited you to join their party!" +
- "\nYou have 60 seconds to accept. Click here to join!\n.*)$");
-
- this.repartying = false;
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(ClientCommandManager.literal("rp").executes(context -> {
- if (!Utils.isOnSkyblock() || this.repartying || client.player == null) return 0;
- this.repartying = true;
- MessageScheduler.INSTANCE.sendMessageAfterCooldown("/p list");
- return 0;
- })));
- }
-
- @Override
- public ChatFilterResult state() {
- return (SkyblockerConfigManager.get().general.acceptReparty || this.repartying) ? ChatFilterResult.FILTER : ChatFilterResult.PASS;
- }
-
- @Override
- public boolean onMatch(Text message, Matcher matcher) {
- if (matcher.group(1) != null && repartying) {
- this.playersSoFar = 0;
- this.players = new String[Integer.parseInt(matcher.group(1)) - 1];
- } else if (matcher.group(2) != null && repartying) {
- Matcher m = PLAYER.matcher(matcher.group(2));
- while (m.find()) {
- this.players[playersSoFar++] = m.group(1);
- }
- } else if (matcher.group("disband") != null && !matcher.group("disband").equals(client.getSession().getUsername())) {
- partyLeader = matcher.group("disband");
- Scheduler.INSTANCE.schedule(() -> partyLeader = null, 61);
- return false;
- } else if (matcher.group("invite") != null && matcher.group("invite").equals(partyLeader)) {
- String command = "/party accept " + partyLeader;
- sendCommand(command, 0);
- return false;
- } else {
- this.repartying = false;
- return false;
- }
- if (this.playersSoFar == this.players.length) {
- reparty();
- }
- return false;
- }
-
- private void reparty() {
- ClientPlayerEntity playerEntity = client.player;
- if (playerEntity == null) {
- this.repartying = false;
- return;
- }
- sendCommand("/p disband", 1);
- for (int i = 0; i < this.players.length; ++i) {
- String command = "/p invite " + this.players[i];
- sendCommand(command, i + 2);
- }
- Scheduler.INSTANCE.schedule(() -> this.repartying = false, this.players.length + 2);
- }
-
- private void sendCommand(String command, int delay) {
- MessageScheduler.INSTANCE.queueMessage(command, delay * BASE_DELAY);
- }
-} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java
deleted file mode 100644
index f614662b..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.utils.Utils;
-import me.xmrvizzy.skyblocker.utils.render.culling.OcclusionCulling;
-import net.minecraft.entity.Entity;
-import net.minecraft.entity.decoration.ArmorStandEntity;
-import net.minecraft.entity.passive.BatEntity;
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.predicate.entity.EntityPredicates;
-import net.minecraft.util.math.Box;
-
-import java.util.List;
-
-public class StarredMobGlow {
-
- public static boolean shouldMobGlow(Entity entity) {
- Box box = entity.getBoundingBox();
-
- if (Utils.isInDungeons() && !entity.isInvisible() && OcclusionCulling.isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) {
- // Minibosses
- if (entity instanceof PlayerEntity) {
- switch (entity.getName().getString()) {
- case "Lost Adventurer", "Shadow Assassin", "Diamond Guy" -> {
- return true;
- }
- }
- }
-
- // Regular Mobs
- if (!(entity instanceof ArmorStandEntity)) {
- Box searchBox = box.expand(0, 2, 0);
- List<ArmorStandEntity> armorStands = entity.getWorld().getEntitiesByClass(ArmorStandEntity.class, searchBox, EntityPredicates.NOT_MOUNTED);
-
- if (!armorStands.isEmpty() && armorStands.get(0).getName().getString().contains("✯")) return true;
- }
-
- // Bats
- return entity instanceof BatEntity;
- }
-
- return false;
- }
-
- public static int getGlowColor(Entity entity) {
- if (entity instanceof PlayerEntity) {
- return switch (entity.getName().getString()) {
- case "Lost Adventurer" -> 0xfee15c;
- case "Shadow Assassin" -> 0x5b2cb2;
- case "Diamond Guy" -> 0x57c2f7;
- default -> 0xf57738;
- };
- }
-
- return 0xf57738;
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java
deleted file mode 100644
index 05ed4d94..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/ThreeWeirdos.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
-import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.entity.decoration.ArmorStandEntity;
-import net.minecraft.text.Text;
-import net.minecraft.util.Formatting;
-
-import java.util.regex.Matcher;
-
-public class ThreeWeirdos extends ChatPatternListener {
- public ThreeWeirdos() {
- super("^§e\\[NPC] §c([A-Z][a-z]+)§f: (?:The reward is(?: not in my chest!|n't in any of our chests\\.)|My chest (?:doesn't have the reward\\. We are all telling the truth\\.|has the reward and I'm telling the truth!)|At least one of them is lying, and the reward is not in §c§c[A-Z][a-z]+'s §rchest\\!|Both of them are telling the truth\\. Also, §c§c[A-Z][a-z]+ §rhas the reward in their chest\\!)$");
- }
-
- @Override
- public ChatFilterResult state() {
- return SkyblockerConfigManager.get().locations.dungeons.solveThreeWeirdos ? null : ChatFilterResult.PASS;
- }
-
- @Override
- public boolean onMatch(Text message, Matcher matcher) {
- MinecraftClient client = MinecraftClient.getInstance();
- if (client.player == null || client.world == null) return false;
- client.world.getEntitiesByClass(
- ArmorStandEntity.class,
- client.player.getBoundingBox().expand(3),
- entity -> {
- Text customName = entity.getCustomName();
- return customName != null && customName.getString().equals(matcher.group(1));
- }
- ).forEach(
- entity -> entity.setCustomName(Text.of(Formatting.GREEN + matcher.group(1)))
- );
- return false;
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java
deleted file mode 100644
index 21493da7..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
-import me.xmrvizzy.skyblocker.utils.tictactoe.TicTacToeUtils;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
-import net.minecraft.block.Block;
-import net.minecraft.block.Blocks;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.decoration.ItemFrameEntity;
-import net.minecraft.item.FilledMapItem;
-import net.minecraft.item.map.MapState;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Box;
-import net.minecraft.util.math.Direction;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
-/**
- * Thanks to Danker for a reference implementation!
- */
-public class TicTacToe {
- private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class);
- private static final float[] RED_COLOR_COMPONENTS = {1.0F, 0.0F, 0.0F};
- private static Box nextBestMoveToMake = null;
-
- public static void init() {
- WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer);
- }
-
- public static void tick() {
- MinecraftClient client = MinecraftClient.getInstance();
- ClientWorld world = client.world;
- ClientPlayerEntity player = client.player;
-
- nextBestMoveToMake = null;
-
- if (world == null || player == null || !Utils.isInDungeons()) return;
-
- //Search within 21 blocks for item frames that contain maps
- Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21);
- List<ItemFrameEntity> itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap);
-
- try {
- //Only attempt to solve if its the player's turn
- if (itemFramesThatHoldMaps.size() != 9 && itemFramesThatHoldMaps.size() % 2 == 1) {
- char[][] board = new char[3][3];
- BlockPos leftmostRow = null;
- int sign = 1;
- char facing = 'X';
-
- for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) {
- MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt()));
-
- if (mapState == null) continue;
-
- int column = 0, row;
- sign = 1;
-
- //Find position of the item frame relative to where it is on the tic tac toe board
- if (itemFrame.getHorizontalFacing() == Direction.SOUTH || itemFrame.getHorizontalFacing() == Direction.WEST) sign = -1;
- BlockPos itemFramePos = BlockPos.ofFloored(itemFrame.getX(), itemFrame.getY(), itemFrame.getZ());
-
- for (int i = 2; i >= 0; i--) {
- int realI = i * sign;
- BlockPos blockPos = itemFramePos;
-
- if (itemFrame.getX() % 0.5 == 0) {
- blockPos = itemFramePos.add(realI, 0, 0);
- } else if (itemFrame.getZ() % 0.5 == 0) {
- blockPos = itemFramePos.add(0, 0, realI);
- facing = 'Z';
- }
-
- Block block = world.getBlockState(blockPos).getBlock();
- if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) {
- leftmostRow = blockPos;
- column = i;
-
- break;
- }
- }
-
- //Determine the row of the item frame
- if (itemFrame.getY() == 72.5) {
- row = 0;
- } else if (itemFrame.getY() == 71.5) {
- row = 1;
- } else if (itemFrame.getY() == 70.5) {
- row = 2;
- } else {
- continue;
- }
-
-
- //Get the color of the middle pixel of the map which determines whether its X or O
- int middleColor = mapState.colors[8256] & 255;
-
- if (middleColor == 114) {
- board[row][column] = 'X';
- } else if (middleColor == 33) {
- board[row][column] = 'O';
- }
-
- int bestMove = TicTacToeUtils.getBestMove(board) - 1;
-
- if (leftmostRow != null) {
- double drawX = facing == 'X' ? leftmostRow.getX() - sign * (bestMove % 3) : leftmostRow.getX();
- double drawY = 72 - (double) (bestMove / 3);
- double drawZ = facing == 'Z' ? leftmostRow.getZ() - sign * (bestMove % 3) : leftmostRow.getZ();
-
- nextBestMoveToMake = new Box(drawX, drawY, drawZ, drawX + 1, drawY + 1, drawZ + 1);
- }
- }
- }
- } catch (Exception e) {
- LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while determining a tic tac toe solution!", e);
- }
- }
-
- private static void solutionRenderer(WorldRenderContext context) {
- try {
- if (SkyblockerConfigManager.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) {
- RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5);
- }
- } catch (Exception e) {
- LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e);
- }
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java
deleted file mode 100644
index b1e51aa6..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/Trivia.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.chat.ChatFilterResult;
-import me.xmrvizzy.skyblocker.utils.chat.ChatPatternListener;
-import me.xmrvizzy.skyblocker.skyblock.FairySouls;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.text.Text;
-import net.minecraft.util.Formatting;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-import java.util.regex.Matcher;
-
-public class Trivia extends ChatPatternListener {
- private static final Map<String, String[]> answers;
- private List<String> solutions = Collections.emptyList();
-
- public Trivia() {
- super("^ +(?:([A-Za-z,' ]*\\?)|§6 ([ⓐⓑⓒ]) §a([a-zA-Z0-9 ]+))$");
- }
-
- @Override
- public ChatFilterResult state() {
- return SkyblockerConfigManager.get().locations.dungeons.solveTrivia ? ChatFilterResult.FILTER : ChatFilterResult.PASS;
- }
-
- @Override
- public boolean onMatch(Text message, Matcher matcher) {
- String riddle = matcher.group(3);
- if (riddle != null) {
- if (!solutions.contains(riddle)) {
- ClientPlayerEntity player = MinecraftClient.getInstance().player;
- if (player != null)
- MinecraftClient.getInstance().player.sendMessage(Text.of(" " + Formatting.GOLD + matcher.group(2) + Formatting.RED + " " + riddle), false);
- return player != null;
- }
- } else updateSolutions(matcher.group(0));
- return false;
- }
-
- private void updateSolutions(String question) {
- String trimmedQuestion = question.trim();
- if (trimmedQuestion.equals("What SkyBlock year is it?")) {
- long currentTime = System.currentTimeMillis() / 1000L;
- long diff = currentTime - 1560276000;
- int year = (int) (diff / 446400 + 1);
- solutions = Collections.singletonList("Year " + year);
- } else {
- solutions = Arrays.asList(answers.get(trimmedQuestion));
- }
- }
-
- static {
- answers = Collections.synchronizedMap(new HashMap<>());
- answers.put("What is the status of The Watcher?", new String[]{"Stalker"});
- answers.put("What is the status of Bonzo?", new String[]{"New Necromancer"});
- answers.put("What is the status of Scarf?", new String[]{"Apprentice Necromancer"});
- answers.put("What is the status of The Professor?", new String[]{"Professor"});
- answers.put("What is the status of Thorn?", new String[]{"Shaman Necromancer"});
- answers.put("What is the status of Livid?", new String[]{"Master Necromancer"});
- answers.put("What is the status of Sadan?", new String[]{"Necromancer Lord"});
- answers.put("What is the status of Maxor?", new String[]{"The Wither Lords"});
- answers.put("What is the status of Goldor?", new String[]{"The Wither Lords"});
- answers.put("What is the status of Storm?", new String[]{"The Wither Lords"});
- answers.put("What is the status of Necron?", new String[]{"The Wither Lords"});
- answers.put("What is the status of Maxor, Storm, Goldor and Necron?", new String[]{"The Wither Lords"});
- answers.put("Which brother is on the Spider's Den?", new String[]{"Rick"});
- answers.put("What is the name of Rick's brother?", new String[]{"Pat"});
- answers.put("What is the name of the Painter in the Hub?", new String[]{"Marco"});
- answers.put("What is the name of the person that upgrades pets?", new String[]{"Kat"});
- answers.put("What is the name of the lady of the Nether?", new String[]{"Elle"});
- answers.put("Which villager in the Village gives you a Rogue Sword?", new String[]{"Jamie"});
- answers.put("How many unique minions are there?", new String[]{"59 Minions"});
- answers.put("Which of these enemies does not spawn in the Spider's Den?", new String[]{"Zombie Spider", "Cave Spider", "Wither Skeleton", "Dashing Spooder", "Broodfather", "Night Spider"});
- answers.put("Which of these monsters only spawns at night?", new String[]{"Zombie Villager", "Ghast"});
- answers.put("Which of these is not a dragon in The End?", new String[]{"Zoomer Dragon", "Weak Dragon", "Stonk Dragon", "Holy Dragon", "Boomer Dragon", "Booger Dragon", "Older Dragon", "Elder Dragon", "Stable Dragon", "Professor Dragon"});
- FairySouls.runAsyncAfterFairySoulsLoad(() -> {
- answers.put("How many total Fairy Souls are there?", getFairySoulsSizeString(null));
- answers.put("How many Fairy Souls are there in Spider's Den?", getFairySoulsSizeString("combat_1"));
- answers.put("How many Fairy Souls are there in The End?", getFairySoulsSizeString("combat_3"));
- answers.put("How many Fairy Souls are there in The Farming Islands?", getFairySoulsSizeString("farming_1"));
- answers.put("How many Fairy Souls are there in Crimson Isle?", getFairySoulsSizeString("crimson_isle"));
- answers.put("How many Fairy Souls are there in The Park?", getFairySoulsSizeString("foraging_1"));
- answers.put("How many Fairy Souls are there in Jerry's Workshop?", getFairySoulsSizeString("winter"));
- answers.put("How many Fairy Souls are there in Hub?", getFairySoulsSizeString("hub"));
- answers.put("How many Fairy Souls are there in The Hub?", getFairySoulsSizeString("hub"));
- answers.put("How many Fairy Souls are there in Deep Caverns?", getFairySoulsSizeString("mining_2"));
- answers.put("How many Fairy Souls are there in Gold Mine?", getFairySoulsSizeString("mining_1"));
- answers.put("How many Fairy Souls are there in Dungeon Hub?", getFairySoulsSizeString("dungeon_hub"));
- });
- }
-
- @NotNull
- private static String[] getFairySoulsSizeString(@Nullable String location) {
- return new String[]{"%d Fairy Souls".formatted(FairySouls.getFairySoulsSize(location))};
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
deleted file mode 100644
index 04cde662..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
+++ /dev/null
@@ -1,275 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
-
-import com.google.gson.JsonObject;
-import it.unimi.dsi.fastutil.ints.IntSortedSet;
-import it.unimi.dsi.fastutil.objects.ObjectIntPair;
-import net.minecraft.block.MapColor;
-import net.minecraft.item.map.MapIcon;
-import net.minecraft.item.map.MapState;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.MathHelper;
-import net.minecraft.util.math.Vec3d;
-import net.minecraft.util.math.Vec3i;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.joml.RoundingMode;
-import org.joml.Vector2i;
-import org.joml.Vector2ic;
-
-import java.util.*;
-
-public class DungeonMapUtils {
- public static final byte BLACK_COLOR = MapColor.BLACK.getRenderColorByte(MapColor.Brightness.LOWEST);
- public static final byte WHITE_COLOR = MapColor.WHITE.getRenderColorByte(MapColor.Brightness.HIGH);
-
- public static byte getColor(MapState map, @Nullable Vector2ic pos) {
- return pos == null ? -1 : getColor(map, pos.x(), pos.y());
- }
-
- public static byte getColor(MapState map, int x, int z) {
- if (x < 0 || z < 0 || x >= 128 || z >= 128) {
- return -1;
- }
- return map.colors[x + (z << 7)];
- }
-
- public static boolean isEntranceColor(MapState map, int x, int z) {
- return getColor(map, x, z) == Room.Type.ENTRANCE.color;
- }
-
- public static boolean isEntranceColor(MapState map, @Nullable Vector2ic pos) {
- return getColor(map, pos) == Room.Type.ENTRANCE.color;
- }
-
- @Nullable
- private static Vector2i getMapPlayerPos(MapState map) {
- for (MapIcon icon : map.getIcons()) {
- if (icon.type() == MapIcon.Type.FRAME) {
- return new Vector2i((icon.x() >> 1) + 64, (icon.z() >> 1) + 64);
- }
- }
- return null;
- }
-
- @Nullable
- public static ObjectIntPair<Vector2ic> getMapEntrancePosAndRoomSize(@NotNull MapState map) {
- Vector2ic mapPos = getMapPlayerPos(map);
- if (mapPos == null) {
- return null;
- }
- Queue<Vector2ic> posToCheck = new ArrayDeque<>();
- Set<Vector2ic> checked = new HashSet<>();
- posToCheck.add(mapPos);
- checked.add(mapPos);
- while ((mapPos = posToCheck.poll()) != null) {
- if (isEntranceColor(map, mapPos)) {
- ObjectIntPair<Vector2ic> mapEntranceAndRoomSizePos = getMapEntrancePosAndRoomSizeAt(map, mapPos);
- if (mapEntranceAndRoomSizePos.rightInt() > 0) {
- return mapEntranceAndRoomSizePos;
- }
- }
- Vector2ic pos = new Vector2i(mapPos).sub(10, 0);
- if (checked.add(pos)) {
- posToCheck.add(pos);
- }
- pos = new Vector2i(mapPos).sub(0, 10);
- if (checked.add(pos)) {
- posToCheck.add(pos);
- }
- pos = new Vector2i(mapPos).add(10, 0);
- if (checked.add(pos)) {
- posToCheck.add(pos);
- }
- pos = new Vector2i(mapPos).add(0, 10);
- if (checked.add(pos)) {
- posToCheck.add(pos);
- }
- }
- return null;
- }
-
- private static ObjectIntPair<Vector2ic> getMapEntrancePosAndRoomSizeAt(MapState map, Vector2ic mapPosImmutable) {
- Vector2i mapPos = new Vector2i(mapPosImmutable);
- // noinspection StatementWithEmptyBody
- while (isEntranceColor(map, mapPos.sub(1, 0))) {
- }
- mapPos.add(1, 0);
- //noinspection StatementWithEmptyBody
- while (isEntranceColor(map, mapPos.sub(0, 1))) {
- }
- return ObjectIntPair.of(mapPos.add(0, 1), getMapRoomSize(map, mapPos));
- }
-
- public static int getMapRoomSize(MapState map, Vector2ic mapEntrancePos) {
- int i = -1;
- //noinspection StatementWithEmptyBody
- while (isEntranceColor(map, mapEntrancePos.x() + ++i, mapEntrancePos.y())) {
- }
- return i > 5 ? i : 0;
- }
-
- /**
- * Gets the map position of the top left corner of the room the player is in.
- *
- * @param map the map
- * @param mapEntrancePos the map position of the top left corner of the entrance
- * @param mapRoomSize the size of a room on the map
- * @return the map position of the top left corner of the room the player is in
- * @implNote {@code mapPos} is shifted by 2 so room borders are evenly split.
- * {@code mapPos} is then shifted by {@code offset} to align the top left most room at (0, 0)
- * so subtracting the modulo will give the top left corner of the room shifted by {@code offset}.
- * Finally, {@code mapPos} is shifted back by {@code offset} to its intended position.
- */
- @Nullable
- public static Vector2ic getMapRoomPos(MapState map, Vector2ic mapEntrancePos, int mapRoomSize) {
- int mapRoomSizeWithGap = mapRoomSize + 4;
- Vector2i mapPos = getMapPlayerPos(map);
- if (mapPos == null) {
- return null;
- }
- Vector2ic offset = new Vector2i(mapEntrancePos.x() % mapRoomSizeWithGap, mapEntrancePos.y() % mapRoomSizeWithGap);
- return mapPos.add(2, 2).sub(offset).sub(mapPos.x() % mapRoomSizeWithGap, mapPos.y() % mapRoomSizeWithGap).add(offset);
- }
-
- /**
- * Gets the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room.
- *
- * @param physicalEntrancePos the physical position of the northwest corner of the entrance room
- * @param mapEntrancePos the map position of the top left corner of the entrance room
- * @param mapRoomSize the size of a room on the map
- * @param physicalPos the physical position of the northwest corner of the room
- * @return the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room
- */
- public static Vector2ic getMapPosFromPhysical(Vector2ic physicalEntrancePos, Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalPos) {
- return new Vector2i(physicalPos).sub(physicalEntrancePos).div(32).mul(mapRoomSize + 4).add(mapEntrancePos);
- }
-
- /**
- * @see #getPhysicalRoomPos(double, double)
- */
- @NotNull
- public static Vector2ic getPhysicalRoomPos(@NotNull Vec3d pos) {
- return getPhysicalRoomPos(pos.getX(), pos.getZ());
- }
-
- /**
- * @see #getPhysicalRoomPos(double, double)
- */
- @NotNull
- public static Vector2ic getPhysicalRoomPos(@NotNull Vec3i pos) {
- return getPhysicalRoomPos(pos.getX(), pos.getZ());
- }
-
- /**
- * Gets the physical position of the northwest corner of the room the given coordinate is in. Hypixel Skyblock Dungeons are aligned to a 32 by 32 blocks grid, allowing corners to be calculated through math.
- *
- * @param x the x position of the coordinate to calculate
- * @param z the z position of the coordinate to calculate
- * @return the physical position of the northwest corner of the room the player is in
- * @implNote {@code physicalPos} is shifted by 0.5 so room borders are evenly split.
- * {@code physicalPos} is further shifted by 8 because Hypixel offset dungeons by 8 blocks in Skyblock 0.12.3.
- * Subtracting the modulo gives the northwest corner of the room shifted by 8. Finally, {@code physicalPos} is shifted back by 8 to its intended position.
- */
- @NotNull
- public static Vector2ic getPhysicalRoomPos(double x, double z) {
- Vector2i physicalPos = new Vector2i(x + 8.5, z + 8.5, RoundingMode.TRUNCATE);
- return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8);
- }
-
- public static Vector2ic[] getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic... mapPositions) {
- for (int i = 0; i < mapPositions.length; i++) {
- mapPositions[i] = getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, mapPositions[i]);
- }
- return mapPositions;
- }
-
- /**
- * Gets the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room.
- *
- * @param mapEntrancePos the map position of the top left corner of the entrance room
- * @param mapRoomSize the size of a room on the map
- * @param physicalEntrancePos the physical position of the northwest corner of the entrance room
- * @param mapPos the map position of the top left corner of the room
- * @return the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room
- */
- public static Vector2ic getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic mapPos) {
- return new Vector2i(mapPos).sub(mapEntrancePos).div(mapRoomSize + 4).mul(32).add(physicalEntrancePos);
- }
-
- public static Vector2ic getPhysicalCornerPos(Room.Direction direction, IntSortedSet segmentsX, IntSortedSet segmentsY) {
- return switch (direction) {
- case NW -> new Vector2i(segmentsX.firstInt(), segmentsY.firstInt());
- case NE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.firstInt());
- case SW -> new Vector2i(segmentsX.firstInt(), segmentsY.lastInt() + 30);
- case SE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.lastInt() + 30);
- };
- }
-
- public static BlockPos actualToRelative(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) {
- return switch (direction) {
- case NW -> new BlockPos(pos.getX() - physicalCornerPos.x(), pos.getY(), pos.getZ() - physicalCornerPos.y());
- case NE -> new BlockPos(pos.getZ() - physicalCornerPos.y(), pos.getY(), -pos.getX() + physicalCornerPos.x());
- case SW -> new BlockPos(-pos.getZ() + physicalCornerPos.y(), pos.getY(), pos.getX() - physicalCornerPos.x());
- case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y());
- };
- }
-
- public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, JsonObject posJson) {
- return relativeToActual(direction, physicalCornerPos, new BlockPos(posJson.get("x").getAsInt(), posJson.get("y").getAsInt(), posJson.get("z").getAsInt()));
- }
-
- public static BlockPos relativeToActual(Room.Direction direction, Vector2ic physicalCornerPos, BlockPos pos) {
- return switch (direction) {
- case NW -> new BlockPos(pos.getX() + physicalCornerPos.x(), pos.getY(), pos.getZ() + physicalCornerPos.y());
- case NE -> new BlockPos(-pos.getZ() + physicalCornerPos.x(), pos.getY(), pos.getX() + physicalCornerPos.y());
- case SW -> new BlockPos(pos.getZ() + physicalCornerPos.x(), pos.getY(), -pos.getX() + physicalCornerPos.y());
- case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y());
- };
- }
-
- public static Room.Type getRoomType(MapState map, Vector2ic mapPos) {
- return switch (getColor(map, mapPos)) {
- case 30 -> Room.Type.ENTRANCE;
- case 63 -> Room.Type.ROOM;
- case 66 -> Room.Type.PUZZLE;
- case 62 -> Room.Type.TRAP;
- case 74 -> Room.Type.MINIBOSS;
- case 82 -> Room.Type.FAIRY;
- case 18 -> Room.Type.BLOOD;
- case 85 -> Room.Type.UNKNOWN;
- default -> null;
- };
- }
-
- public static Vector2ic[] getRoomSegments(MapState map, Vector2ic mapPos, int mapRoomSize, byte color) {
- Set<Vector2ic> segments = new HashSet<>();
- Queue<Vector2ic> queue = new ArrayDeque<>();
- segments.add(mapPos);
- queue.add(mapPos);
- while (!queue.isEmpty()) {
- Vector2ic curMapPos = queue.poll();
- Vector2i newMapPos = new Vector2i();
- if (getColor(map, newMapPos.set(curMapPos).sub(1, 0)) == color && !segments.contains(newMapPos.sub(mapRoomSize + 3, 0))) {
- segments.add(newMapPos);
- queue.add(newMapPos);
- newMapPos = new Vector2i();
- }
- if (getColor(map, newMapPos.set(curMapPos).sub(0, 1)) == color && !segments.contains(newMapPos.sub(0, mapRoomSize + 3))) {
- segments.add(newMapPos);
- queue.add(newMapPos);
- newMapPos = new Vector2i();
- }
- if (getColor(map, newMapPos.set(curMapPos).add(mapRoomSize, 0)) == color && !segments.contains(newMapPos.add(4, 0))) {
- segments.add(newMapPos);
- queue.add(newMapPos);
- newMapPos = new Vector2i();
- }
- if (getColor(map, newMapPos.set(curMapPos).add(0, mapRoomSize)) == color && !segments.contains(newMapPos.add(0, 4))) {
- segments.add(newMapPos);
- queue.add(newMapPos);
- }
- }
- DungeonSecrets.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray()));
- return segments.toArray(Vector2ic[]::new);
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
deleted file mode 100644
index 4e4e9565..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
+++ /dev/null
@@ -1,451 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
-
-import com.google.gson.JsonArray;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.mojang.brigadier.Command;
-import com.mojang.brigadier.arguments.IntegerArgumentType;
-import com.mojang.brigadier.builder.ArgumentBuilder;
-import com.mojang.brigadier.builder.RequiredArgumentBuilder;
-import it.unimi.dsi.fastutil.objects.Object2ByteMap;
-import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
-import it.unimi.dsi.fastutil.objects.ObjectIntPair;
-import me.xmrvizzy.skyblocker.SkyblockerMod;
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.Utils;
-import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
-import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
-import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
-import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
-import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
-import net.fabricmc.fabric.api.event.player.UseBlockCallback;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.entity.ItemEntity;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.entity.mob.AmbientEntity;
-import net.minecraft.entity.passive.BatEntity;
-import net.minecraft.item.FilledMapItem;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.item.map.MapState;
-import net.minecraft.resource.Resource;
-import net.minecraft.text.Text;
-import net.minecraft.util.ActionResult;
-import net.minecraft.util.Identifier;
-import net.minecraft.util.hit.BlockHitResult;
-import net.minecraft.util.math.Vec3d;
-import net.minecraft.world.World;
-import org.jetbrains.annotations.Contract;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-import org.joml.Vector2ic;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.ObjectInputStream;
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.zip.InflaterInputStream;
-
-import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
-import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
-
-public class DungeonSecrets {
- protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class);
- private static final String DUNGEONS_PATH = "dungeons";
- /**
- * Maps the block identifier string to a custom numeric block id used in dungeon rooms data.
- *
- * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft.
- */
- @SuppressWarnings("JavadocReference")
- protected static final Object2ByteMap<String> NUMERIC_ID = new Object2ByteOpenHashMap<>(Map.ofEntries(
- Map.entry("minecraft:stone", (byte) 1),
- Map.entry("minecraft:diorite", (byte) 2),
- Map.entry("minecraft:polished_diorite", (byte) 3),
- Map.entry("minecraft:andesite", (byte) 4),
- Map.entry("minecraft:polished_andesite", (byte) 5),
- Map.entry("minecraft:grass_block", (byte) 6),
- Map.entry("minecraft:dirt", (byte) 7),
- Map.entry("minecraft:coarse_dirt", (byte) 8),
- Map.entry("minecraft:cobblestone", (byte) 9),
- Map.entry("minecraft:bedrock", (byte) 10),
- Map.entry("minecraft:oak_leaves", (byte) 11),
- Map.entry("minecraft:gray_wool", (byte) 12),
- Map.entry("minecraft:double_stone_slab", (byte) 13),
- Map.entry("minecraft:mossy_cobblestone", (byte) 14),
- Map.entry("minecraft:clay", (byte) 15),
- Map.entry("minecraft:stone_bricks", (byte) 16),
- Map.entry("minecraft:mossy_stone_bricks", (byte) 17),
- Map.entry("minecraft:chiseled_stone_bricks", (byte) 18),
- Map.entry("minecraft:gray_terracotta", (byte) 19),
- Map.entry("minecraft:cyan_terracotta", (byte) 20),
- Map.entry("minecraft:black_terracotta", (byte) 21)
- ));
- /**
- * Block data for dungeon rooms. See {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU} for format details and how it's generated.
- * All access to this map must check {@link #isRoomsLoaded()} to prevent concurrent modification.
- */
- @SuppressWarnings("JavadocReference")
- protected static final HashMap<String, Map<String, Map<String, int[]>>> ROOMS_DATA = new HashMap<>();
- @NotNull
- private static final Map<Vector2ic, Room> rooms = new HashMap<>();
- private static final Map<String, JsonElement> roomsJson = new HashMap<>();
- private static final Map<String, JsonElement> waypointsJson = new HashMap<>();
- @Nullable
- private static CompletableFuture<Void> roomsLoaded;
- /**
- * The map position of the top left corner of the entrance room.
- */
- @Nullable
- private static Vector2ic mapEntrancePos;
- /**
- * The size of a room on the map.
- */
- private static int mapRoomSize;
- /**
- * The physical position of the northwest corner of the entrance room.
- */
- @Nullable
- private static Vector2ic physicalEntrancePos;
- private static Room currentRoom;
-
- public static boolean isRoomsLoaded() {
- return roomsLoaded != null && roomsLoaded.isDone();
- }
-
- @SuppressWarnings("unused")
- public static JsonObject getRoomMetadata(String room) {
- return roomsJson.get(room).getAsJsonObject();
- }
-
- public static JsonArray getRoomWaypoints(String room) {
- return waypointsJson.get(room).getAsJsonArray();
- }
-
- /**
- * Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}.
- * Use {@link #isRoomsLoaded()} to check for completion of loading.
- */
- public static void init() {
- if (SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.noInitSecretWaypoints) {
- return;
- }
- // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set
- CompletableFuture.runAsync(DungeonSecrets::load, MinecraftClient.getInstance()).exceptionally(e -> {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
- return null;
- });
- Scheduler.INSTANCE.scheduleCyclic(DungeonSecrets::update, 10);
- WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render);
- ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage);
- ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage);
- UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> onUseBlock(world, hitResult));
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("secrets")
- .then(literal("markAsFound").then(markSecretsCommand(true)))
- .then(literal("markAsMissing").then(markSecretsCommand(false)))))));
- ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset()));
- }
-
- private static void load() {
- long startTime = System.currentTimeMillis();
- List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>();
- for (Map.Entry<Identifier, Resource> resourceEntry : MinecraftClient.getInstance().getResourceManager().findResources(DUNGEONS_PATH, id -> id.getPath().endsWith(".skeleton")).entrySet()) {
- String[] path = resourceEntry.getKey().getPath().split("/");
- if (path.length != 4) {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets, invalid resource identifier {}", resourceEntry.getKey());
- break;
- }
- String dungeon = path[1];
- String roomShape = path[2];
- String room = path[3].substring(0, path[3].length() - ".skeleton".length());
- ROOMS_DATA.computeIfAbsent(dungeon, dungeonKey -> new HashMap<>());
- ROOMS_DATA.get(dungeon).computeIfAbsent(roomShape, roomShapeKey -> new HashMap<>());
- dungeonFutures.add(CompletableFuture.supplyAsync(() -> readRoom(resourceEntry.getValue())).thenAcceptAsync(rooms -> {
- Map<String, int[]> roomsMap = ROOMS_DATA.get(dungeon).get(roomShape);
- synchronized (roomsMap) {
- roomsMap.put(room, rooms);
- }
- LOGGER.debug("[Skyblocker] Loaded dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room);
- }).exceptionally(e -> {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room, e);
- return null;
- }));
- }
- dungeonFutures.add(CompletableFuture.runAsync(() -> {
- try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) {
- loadJson(roomsReader, roomsJson);
- loadJson(waypointsReader, waypointsJson);
- LOGGER.debug("[Skyblocker] Loaded dungeon secrets json");
- } catch (Exception e) {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets json", e);
- }
- }));
- roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker] Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total in {} ms", ROOMS_DATA.size(), ROOMS_DATA.values().stream().mapToInt(Map::size).sum(), ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).mapToInt(Map::size).sum(), System.currentTimeMillis() - startTime)).exceptionally(e -> {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
- return null;
- });
- LOGGER.info("[Skyblocker] Started loading dungeon secrets in (blocked main thread for) {} ms", System.currentTimeMillis() - startTime);
- }
-
- private static int[] readRoom(Resource resource) throws RuntimeException {
- try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(resource.getInputStream()))) {
- return (int[]) in.readObject();
- } catch (IOException | ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Loads the json from the given {@link BufferedReader} into the given {@link Map}.
- * @param reader the reader to read the json from
- * @param map the map to load into
- */
- private static void loadJson(BufferedReader reader, Map<String, JsonElement> map) {
- SkyblockerMod.GSON.fromJson(reader, JsonObject.class).asMap().forEach((room, jsonElement) -> map.put(room.toLowerCase().replaceAll(" ", "-"), jsonElement));
- }
-
- private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, Integer>> markSecretsCommand(boolean found) {
- return argument("secret", IntegerArgumentType.integer()).executes(context -> {
- int secretIndex = IntegerArgumentType.getInteger(context, "secret");
- if (markSecrets(secretIndex, found)) {
- context.getSource().sendFeedback(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFound" : "skyblocker.dungeons.secrets.markSecretMissing", secretIndex));
- } else {
- context.getSource().sendError(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFoundUnable" : "skyblocker.dungeons.secrets.markSecretMissingUnable", secretIndex));
- }
- return Command.SINGLE_SUCCESS;
- });
- }
-
- /**
- * Updates the dungeon. The general idea is similar to the Dungeon Rooms Mod.
- * <p></p>
- * When entering a new dungeon, this method:
- * <ul>
- * <li> Gets the physical northwest corner position of the entrance room and saves it in {@link #physicalEntrancePos}. </li>
- * <li> Do nothing until the dungeon map exists. </li>
- * <li> Gets the upper left corner of entrance room on the map and saves it in {@link #mapEntrancePos}. </li>
- * <li> Gets the size of a room on the map in pixels and saves it in {@link #mapRoomSize}. </li>
- * <li> Creates a new {@link Room} with {@link Room.Type} {@link Room.Type.ENTRANCE ENTRANCE} and sets {@link #currentRoom}. </li>
- * </ul>
- * When processing an existing dungeon, this method:
- * <ul>
- * <li> Calculates the physical northwest corner and upper left corner on the map of the room the player is currently in. </li>
- * <li> Gets the room type based on the map color. </li>
- * <li> If the room has not been created (when the physical northwest corner is not in {@link #rooms}):</li>
- * <ul>
- * <li> If the room type is {@link Room.Type.ROOM}, gets the northwest corner of all connected room segments with {@link DungeonMapUtils#getRoomSegments(MapState, Vector2ic, int, byte)}. (For example, a 1x2 room has two room segments.) </li>
- * <li> Create a new room. </li>
- * </ul>
- * <li> Sets {@link #currentRoom} to the current room, either created from the previous step or from {@link #rooms}. </li>
- * <li> Calls {@link Room#update()} on {@link #currentRoom}. </li>
- * </ul>
- */
- @SuppressWarnings("JavadocReference")
- private static void update() {
- if (!SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints) {
- return;
- }
- if (!Utils.isInDungeons()) {
- if (mapEntrancePos != null) {
- reset();
- }
- return;
- }
- MinecraftClient client = MinecraftClient.getInstance();
- ClientPlayerEntity player = client.player;
- if (player == null || client.world == null) {
- return;
- }
- if (physicalEntrancePos == null) {
- Vec3d playerPos = player.getPos();
- physicalEntrancePos = DungeonMapUtils.getPhysicalRoomPos(playerPos);
- currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos);
- }
- ItemStack stack = player.getInventory().main.get(8);
- if (!stack.isOf(Items.FILLED_MAP)) {
- return;
- }
- MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world);
- if (map == null) {
- return;
- }
- if (mapEntrancePos == null || mapRoomSize == 0) {
- ObjectIntPair<Vector2ic> mapEntrancePosAndSize = DungeonMapUtils.getMapEntrancePosAndRoomSize(map);
- if (mapEntrancePosAndSize == null) {
- return;
- }
- mapEntrancePos = mapEntrancePosAndSize.left();
- mapRoomSize = mapEntrancePosAndSize.rightInt();
- LOGGER.info("[Skyblocker] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos);
- }
-
- Vector2ic physicalPos = DungeonMapUtils.getPhysicalRoomPos(client.player.getPos());
- Vector2ic mapPos = DungeonMapUtils.getMapPosFromPhysical(physicalEntrancePos, mapEntrancePos, mapRoomSize, physicalPos);
- Room room = rooms.get(physicalPos);
- if (room == null) {
- Room.Type type = DungeonMapUtils.getRoomType(map, mapPos);
- if (type == null || type == Room.Type.UNKNOWN) {
- return;
- }
- switch (type) {
- case ENTRANCE, PUZZLE, TRAP, MINIBOSS, FAIRY, BLOOD -> room = newRoom(type, physicalPos);
- case ROOM -> room = newRoom(type, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, mapPos, mapRoomSize, type.color)));
- }
- }
- if (room != null && currentRoom != room) {
- currentRoom = room;
- }
- currentRoom.update();
- }
-
- /**
- * Creates a new room with the given type and physical positions,
- * adds the room to {@link #rooms}, and sets {@link #currentRoom} to the new room.
- *
- * @param type the type of room to create
- * @param physicalPositions the physical positions of the room
- */
- @Nullable
- private static Room newRoom(Room.Type type, Vector2ic... physicalPositions) {
- try {
- Room newRoom = new Room(type, physicalPositions);
- for (Vector2ic physicalPos : physicalPositions) {
- rooms.put(physicalPos, newRoom);
- }
- return newRoom;
- } catch (IllegalArgumentException e) {
- LOGGER.error("[Skyblocker] Failed to create room", e);
- }
- return null;
- }
-
- /**
- * Renders the secret waypoints in {@link #currentRoom} if {@link #isCurrentRoomMatched()}.
- */
- private static void render(WorldRenderContext context) {
- if (isCurrentRoomMatched()) {
- currentRoom.render(context);
- }
- }
-
- /**
- * Calls {@link Room#onChatMessage(String)} on {@link #currentRoom} if the message is an overlay message and {@link #isCurrentRoomMatched()}.
- * Used to detect when all secrets in a room are found.
- */
- private static void onChatMessage(Text text, boolean overlay) {
- if (overlay && isCurrentRoomMatched()) {
- currentRoom.onChatMessage(text.getString());
- }
- }
-
- /**
- * Calls {@link Room#onUseBlock(World, BlockHitResult)} on {@link #currentRoom} if {@link #isCurrentRoomMatched()}.
- * Used to detect finding {@link SecretWaypoint.Category.CHEST} and {@link SecretWaypoint.Category.WITHER} secrets.
- *
- * @return {@link ActionResult#PASS}
- */
- @SuppressWarnings("JavadocReference")
- private static ActionResult onUseBlock(World world, BlockHitResult hitResult) {
- if (isCurrentRoomMatched()) {
- currentRoom.onUseBlock(world, hitResult);
- }
- return ActionResult.PASS;
- }
-
- /**
- * Calls {@link Room#onItemPickup(ItemEntity, LivingEntity)} on the room the {@code collector} is in if that room {@link #isRoomMatched(Room)}.
- * Used to detect finding {@link SecretWaypoint.Category.ITEM} secrets.
- * If the collector is the player, {@link #currentRoom} is used as an optimization.
- */
- @SuppressWarnings("JavadocReference")
- public static void onItemPickup(ItemEntity itemEntity, LivingEntity collector, boolean isPlayer) {
- if (isPlayer) {
- if (isCurrentRoomMatched()) {
- currentRoom.onItemPickup(itemEntity, collector);
- }
- } else {
- Room room = getRoomAtPhysical(collector.getPos());
- if (isRoomMatched(room)) {
- room.onItemPickup(itemEntity, collector);
- }
- }
- }
-
- /**
- * Calls {@link Room#onBatRemoved(BatEntity)} on the room the {@code bat} is in if that room {@link #isRoomMatched(Room)}.
- * Used to detect finding {@link SecretWaypoint.Category.BAT} secrets.
- */
- @SuppressWarnings("JavadocReference")
- public static void onBatRemoved(AmbientEntity bat) {
- Room room = getRoomAtPhysical(bat.getPos());
- if (isRoomMatched(room)) {
- room.onBatRemoved(bat);
- }
- }
-
- public static boolean markSecrets(int secretIndex, boolean found) {
- if (isCurrentRoomMatched()) {
- return currentRoom.markSecrets(secretIndex, found);
- }
- return false;
- }
-
- /**
- * Gets the room at the given physical position.
- *
- * @param pos the physical position
- * @return the room at the given physical position, or null if there is no room at the given physical position
- * @see #rooms
- * @see DungeonMapUtils#getPhysicalRoomPos(Vec3d)
- */
- @Nullable
- private static Room getRoomAtPhysical(Vec3d pos) {
- return rooms.get(DungeonMapUtils.getPhysicalRoomPos(pos));
- }
-
- /**
- * Calls {@link #isRoomMatched(Room)} on {@link #currentRoom}.
- *
- * @return {@code true} if {@link #currentRoom} is not null and {@link #isRoomMatched(Room)}
- */
- private static boolean isCurrentRoomMatched() {
- return isRoomMatched(currentRoom);
- }
-
- /**
- * Calls {@link #shouldProcess()} and {@link Room#isMatched()} on the given room.
- *
- * @param room the room to check
- * @return {@code true} if {@link #shouldProcess()}, the given room is not null, and {@link Room#isMatched()} on the given room
- */
- @Contract("null -> false")
- private static boolean isRoomMatched(@Nullable Room room) {
- return shouldProcess() && room != null && room.isMatched();
- }
-
- /**
- * Checks if the player is in a dungeon and {@link me.xmrvizzy.skyblocker.config.SkyblockerConfigManager.Dungeons#secretWaypoints Secret Waypoints} is enabled.
- *
- * @return whether dungeon secrets should be processed
- */
- private static boolean shouldProcess() {
- return SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints && Utils.isInDungeons();
- }
-
- /**
- * Resets fields when leaving a dungeon.
- */
- private static void reset() {
- mapEntrancePos = null;
- mapRoomSize = 0;
- physicalEntrancePos = null;
- rooms.clear();
- currentRoom = null;
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
deleted file mode 100644
index ae5afaa3..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java
+++ /dev/null
@@ -1,473 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
-
-import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableTable;
-import com.google.common.collect.Table;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
-import it.unimi.dsi.fastutil.ints.IntSortedSet;
-import it.unimi.dsi.fastutil.ints.IntSortedSets;
-import me.xmrvizzy.skyblocker.utils.scheduler.Scheduler;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.fabricmc.fabric.api.util.TriState;
-import net.minecraft.block.BlockState;
-import net.minecraft.block.Blocks;
-import net.minecraft.block.MapColor;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.client.world.ClientWorld;
-import net.minecraft.entity.ItemEntity;
-import net.minecraft.entity.LivingEntity;
-import net.minecraft.entity.mob.AmbientEntity;
-import net.minecraft.registry.Registries;
-import net.minecraft.util.hit.BlockHitResult;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.MathHelper;
-import net.minecraft.world.World;
-import org.apache.commons.lang3.tuple.MutableTriple;
-import org.apache.commons.lang3.tuple.Triple;
-import org.jetbrains.annotations.NotNull;
-import org.joml.Vector2i;
-import org.joml.Vector2ic;
-
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Room {
- private static final Pattern SECRETS = Pattern.compile("§7(\\d{1,2})/(\\d{1,2}) Secrets");
- @NotNull
- private final Type type;
- @NotNull
- private final Set<Vector2ic> segments;
- /**
- * The shape of the room. See {@link #getShape(IntSortedSet, IntSortedSet)}.
- */
- @NotNull
- private final Shape shape;
- /**
- * The room data containing all rooms for a specific dungeon and {@link #shape}.
- */
- private Map<String, int[]> roomsData;
- /**
- * Contains all possible dungeon rooms for this room. The list is gradually shrunk by checking blocks until only one room is left.
- */
- private List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms;
- /**
- * Contains all blocks that have been checked to prevent checking the same block multiple times.
- */
- private Set<BlockPos> checkedBlocks = new HashSet<>();
- /**
- * The task that is used to check blocks. This is used to ensure only one such task can run at a time.
- */
- private CompletableFuture<Void> findRoom;
- private int doubleCheckBlocks;
- /**
- * Represents the matching state of the room with the following possible values:
- * <li>{@link TriState#DEFAULT} means that the room has not been checked, is being processed, or does not {@link Type#needsScanning() need to be processed}.
- * <li>{@link TriState#FALSE} means that the room has been checked and there is no match.
- * <li>{@link TriState#TRUE} means that the room has been checked and there is a match.
- */
- private TriState matched = TriState.DEFAULT;
- private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints;
-
- public Room(@NotNull Type type, @NotNull Vector2ic... physicalPositions) {
- this.type = type;
- segments = Set.of(physicalPositions);
- IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
- IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
- shape = getShape(segmentsX, segmentsY);
- roomsData = DungeonSecrets.ROOMS_DATA.getOrDefault("catacombs", Collections.emptyMap()).getOrDefault(shape.shape.toLowerCase(), Collections.emptyMap());
- possibleRooms = getPossibleRooms(segmentsX, segmentsY);
- }
-
- @NotNull
- public Type getType() {
- return type;
- }
-
- public boolean isMatched() {
- return matched == TriState.TRUE;
- }
-
- @Override
- public String toString() {
- return "Room{type=" + type + ", shape=" + shape + ", matched=" + matched + ", segments=" + Arrays.toString(segments.toArray()) + "}";
- }
-
- @NotNull
- private Shape getShape(IntSortedSet segmentsX, IntSortedSet segmentsY) {
- return switch (segments.size()) {
- case 1 -> Shape.ONE_BY_ONE;
- case 2 -> Shape.ONE_BY_TWO;
- case 3 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.L_SHAPE : Shape.ONE_BY_THREE;
- case 4 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.TWO_BY_TWO : Shape.ONE_BY_FOUR;
- default -> throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray()));
- };
- }
-
- private List<MutableTriple<Direction, Vector2ic, List<String>>> getPossibleRooms(IntSortedSet segmentsX, IntSortedSet segmentsY) {
- List<String> possibleDirectionRooms = new ArrayList<>(roomsData.keySet());
- List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms = new ArrayList<>();
- for (Direction direction : getPossibleDirections(segmentsX, segmentsY)) {
- possibleRooms.add(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), possibleDirectionRooms));
- }
- return possibleRooms;
- }
-
- @NotNull
- private Direction[] getPossibleDirections(IntSortedSet segmentsX, IntSortedSet segmentsY) {
- return switch (shape) {
- case ONE_BY_ONE, TWO_BY_TWO -> Direction.values();
- case ONE_BY_TWO, ONE_BY_THREE, ONE_BY_FOUR -> {
- if (segmentsX.size() > 1 && segmentsY.size() == 1) {
- yield new Direction[]{Direction.NW, Direction.SE};
- } else if (segmentsX.size() == 1 && segmentsY.size() > 1) {
- yield new Direction[]{Direction.NE, Direction.SW};
- }
- throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray()));
- }
- case L_SHAPE -> {
- if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()))) {
- yield new Direction[]{Direction.SW};
- } else if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.lastInt()))) {
- yield new Direction[]{Direction.SE};
- } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.firstInt()))) {
- yield new Direction[]{Direction.NW};
- } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.lastInt()))) {
- yield new Direction[]{Direction.NE};
- }
- throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray()));
- }
- };
- }
-
- /**
- * Updates the room.
- * <p></p>
- * This method returns immediately if any of the following conditions are met:
- * <ul>
- * <li> The room does not need to be scanned and matched. (When the room is not of type {@link Type.ROOM}, {@link Type.PUZZLE}, or {@link Type.TRAP}. See {@link Type#needsScanning()}) </li>
- * <li> The room has been matched or failed to match and is on cooldown. See {@link #matched}. </li>
- * <li> {@link #findRoom The previous update} has not completed. </li>
- * </ul>
- * Then this method tries to match this room through:
- * <ul>
- * <li> Iterate over a 11 by 11 by 11 box around the player. </li>
- * <li> Check it the block is part of this room and not part of a doorway. See {@link #segments} and {@link #notInDoorway(BlockPos)}. </li>
- * <li> Checks if the position has been checked and adds it to {@link #checkedBlocks}. </li>
- * <li> Calls {@link #checkBlock(ClientWorld, BlockPos)} </li>
- * </ul>
- */
- @SuppressWarnings("JavadocReference")
- protected void update() {
- // Logical AND has higher precedence than logical OR
- if (!type.needsScanning() || matched != TriState.DEFAULT || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) {
- return;
- }
- MinecraftClient client = MinecraftClient.getInstance();
- ClientPlayerEntity player = client.player;
- ClientWorld world = client.world;
- if (player == null || world == null) {
- return;
- }
- findRoom = CompletableFuture.runAsync(() -> {
- for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) {
- if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(world, pos)) {
- break;
- }
- }
- });
- }
-
- private static boolean notInDoorway(BlockPos pos) {
- if (pos.getY() < 66 || pos.getY() > 73) {
- return true;
- }
- int x = MathHelper.floorMod(pos.getX() - 8, 32);
- int z = MathHelper.floorMod(pos.getZ() - 8, 32);
- return (x < 13 || x > 17 || z > 2 && z < 28) && (z < 13 || z > 17 || x > 2 && x < 28);
- }
-
- /**
- * Filters out dungeon rooms which does not contain the block at the given position.
- * <p></p>
- * This method:
- * <ul>
- * <li> Checks if the block type is included in the dungeon rooms data. See {@link DungeonSecrets#NUMERIC_ID}. </li>
- * <li> For each possible direction: </li>
- * <ul>
- * <li> Rotate and convert the position to a relative position. See {@link DungeonMapUtils#actualToRelative(Direction, Vector2ic, BlockPos)}. </li>
- * <li> Encode the block based on the relative position and the custom numeric block id. See {@link #posIdToInt(BlockPos, byte)}. </li>
- * <li> For each possible room in the current direction: </li>
- * <ul>
- * <li> Check if {@link #roomsData} contains the encoded block. </li>
- * <li> If so, add the room to the new list of possible rooms for this direction. </li>
- * </ul>
- * <li> Replace the old possible room list for the current direction with the new one. </li>
- * </ul>
- * <li> If there are no matching rooms left: </li>
- * <ul>
- * <li> Terminate matching by setting {@link #matched} to {@link TriState#FALSE}. </li>
- * <li> Schedule another matching attempt in 50 ticks (2.5 seconds). </li>
- * <li> Reset {@link #possibleRooms} and {@link #checkedBlocks} with {@link #reset()}. </li>
- * <li> Return {@code true} </li>
- * </ul>
- * <li> If there are exactly one room matching: </li>
- * <ul>
- * <li> Call {@link #roomMatched(String, Direction, Vector2ic)}. </li>
- * <li> Discard the no longer needed fields to save memory. </li>
- * <li> Return {@code true} </li>
- * </ul>
- * <li> Return {@code false} </li>
- * </ul>
- *
- * @param world the world to get the block from
- * @param pos the position of the block to check
- * @return whether room matching should end. Either a match is found or there are no valid rooms left
- */
- private boolean checkBlock(ClientWorld world, BlockPos pos) {
- byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString());
- if (id == 0) {
- return false;
- }
- for (MutableTriple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
- int block = posIdToInt(DungeonMapUtils.actualToRelative(directionRooms.getLeft(), directionRooms.getMiddle(), pos), id);
- List<String> possibleDirectionRooms = new ArrayList<>();
- for (String room : directionRooms.getRight()) {
- if (Arrays.binarySearch(roomsData.get(room), block) >= 0) {
- possibleDirectionRooms.add(room);
- }
- }
- directionRooms.setRight(possibleDirectionRooms);
- }
-
- int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum();
- if (matchingRoomsSize == 0) {
- // If no rooms match, reset the fields and scan again after 50 ticks.
- matched = TriState.FALSE;
- DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size());
- Scheduler.INSTANCE.schedule(() -> matched = TriState.DEFAULT, 50);
- reset();
- return true;
- } else if (matchingRoomsSize == 1 && ++doubleCheckBlocks >= 10) {
- // If one room matches, load the secrets for that room and discard the no longer needed fields.
- for (Triple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
- if (directionRooms.getRight().size() == 1) {
- roomMatched(directionRooms.getRight().get(0), directionRooms.getLeft(), directionRooms.getMiddle());
- discard();
- return true;
- }
- }
- return false; // This should never happen, we just checked that there is one possible room, and the return true in the loop should activate
- } else {
- DungeonSecrets.LOGGER.debug("[Skyblocker] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size());
- return false;
- }
- }
-
- /**
- * Encodes a {@link BlockPos} and the custom numeric block id into an integer.
- *
- * @param pos the position of the block
- * @param id the custom numeric block id
- * @return the encoded integer
- */
- private int posIdToInt(BlockPos pos, byte id) {
- return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id;
- }
-
- /**
- * Loads the secret waypoints for the room from {@link DungeonSecrets#waypointsJson} once it has been matched
- * and sets {@link #matched} to {@link TriState#TRUE}.
- *
- * @param directionRooms the direction, position, and name of the room
- */
- @SuppressWarnings("JavadocReference")
- private void roomMatched(String name, Direction direction, Vector2ic physicalCornerPos) {
- Table<Integer, BlockPos, SecretWaypoint> secretWaypointsMutable = HashBasedTable.create();
- for (JsonElement waypointElement : DungeonSecrets.getRoomWaypoints(name)) {
- JsonObject waypoint = waypointElement.getAsJsonObject();
- String secretName = waypoint.get("secretName").getAsString();
- int secretIndex = Integer.parseInt(secretName.substring(0, Character.isDigit(secretName.charAt(1)) ? 2 : 1));
- BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint);
- secretWaypointsMutable.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos));
- }
- secretWaypoints = ImmutableTable.copyOf(secretWaypointsMutable);
- matched = TriState.TRUE;
- DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size());
- }
-
- /**
- * Resets fields for another round of matching after room matching fails.
- */
- private void reset() {
- IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
- IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
- possibleRooms = getPossibleRooms(segmentsX, segmentsY);
- checkedBlocks = new HashSet<>();
- doubleCheckBlocks = 0;
- }
-
- /**
- * Discards fields after room matching completes when a room is found.
- * These fields are no longer needed and are discarded to save memory.
- */
- private void discard() {
- roomsData = null;
- possibleRooms = null;
- checkedBlocks = null;
- doubleCheckBlocks = 0;
- }
-
- /**
- * Calls {@link SecretWaypoint#render(WorldRenderContext)} on {@link #secretWaypoints all secret waypoints}.
- */
- protected void render(WorldRenderContext context) {
- for (SecretWaypoint secretWaypoint : secretWaypoints.values()) {
- if (secretWaypoint.shouldRender()) {
- secretWaypoint.render(context);
- }
- }
- }
-
- /**
- * Sets all secrets as found if {@link #isAllSecretsFound(String)}.
- */
- protected void onChatMessage(String message) {
- if (isAllSecretsFound(message)) {
- secretWaypoints.values().forEach(SecretWaypoint::setFound);
- }
- }
-
- /**
- * Checks if the number of found secrets is equals or greater than the total number of secrets in the room.
- *
- * @param message the message to check in
- * @return whether the number of found secrets is equals or greater than the total number of secrets in the room
- */
- protected static boolean isAllSecretsFound(String message) {
- Matcher matcher = SECRETS.matcher(message);
- if (matcher.find()) {
- return Integer.parseInt(matcher.group(1)) >= Integer.parseInt(matcher.group(2));
- }
- return false;
- }
-
- /**
- * Marks the secret at the interaction position as found when the player interacts with a chest or a player head,
- * if there is a secret at the interaction position.
- *
- * @param world the world to get the block from
- * @param hitResult the block being interacted with
- * @see #onSecretFound(SecretWaypoint, String, Object...)
- */
- protected void onUseBlock(World world, BlockHitResult hitResult) {
- BlockState state = world.getBlockState(hitResult.getBlockPos());
- if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) {
- secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::needsInteraction).findAny()
- .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex));
- } else if (state.isOf(Blocks.LEVER)) {
- secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound);
- }
- }
-
- /**
- * Marks the closest secret that requires item pickup no greater than 6 blocks away as found when the player picks up a secret item.
- *
- * @param itemEntity the item entity being picked up
- * @param collector the collector of the item
- * @see #onSecretFound(SecretWaypoint, String, Object...)
- */
- protected void onItemPickup(ItemEntity itemEntity, LivingEntity collector) {
- if (SecretWaypoint.SECRET_ITEMS.stream().noneMatch(itemEntity.getStack().getName().getString()::contains)) {
- return;
- }
- secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(collector))).filter(SecretWaypoint.getRangePredicate(collector))
- .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
- }
-
- /**
- * Marks the closest bat secret as found when a bat is killed.
- *
- * @param bat the bat being killed
- * @see #onSecretFound(SecretWaypoint, String, Object...)
- */
- protected void onBatRemoved(AmbientEntity bat) {
- secretWaypoints.values().stream().filter(SecretWaypoint::isBat).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(bat)))
- .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
- }
-
- /**
- * Marks all secret waypoints with the same index as the given {@link SecretWaypoint} as found.
- *
- * @param secretWaypoint the secret waypoint to read the index from.
- * @param msg the message to log
- * @param args the args for the {@link org.slf4j.Logger#info(String, Object...) Logger#info(String, Object...)} call
- */
- private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) {
- secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound);
- DungeonSecrets.LOGGER.info(msg, args);
- }
-
- protected boolean markSecrets(int secretIndex, boolean found) {
- Map<BlockPos, SecretWaypoint> secret = secretWaypoints.row(secretIndex);
- if (secret.isEmpty()) {
- return false;
- } else {
- secret.values().forEach(found ? SecretWaypoint::setFound : SecretWaypoint::setMissing);
- return true;
- }
- }
-
- public enum Type {
- ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)),
- ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)),
- PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)),
- TRAP(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.HIGH)),
- MINIBOSS(MapColor.YELLOW.getRenderColorByte(MapColor.Brightness.HIGH)),
- FAIRY(MapColor.PINK.getRenderColorByte(MapColor.Brightness.HIGH)),
- BLOOD(MapColor.BRIGHT_RED.getRenderColorByte(MapColor.Brightness.HIGH)),
- UNKNOWN(MapColor.GRAY.getRenderColorByte(MapColor.Brightness.NORMAL));
- final byte color;
-
- Type(byte color) {
- this.color = color;
- }
-
- /**
- * @return whether this room type has secrets and needs to be scanned and matched.
- */
- private boolean needsScanning() {
- return switch (this) {
- case ROOM, PUZZLE, TRAP -> true;
- default -> false;
- };
- }
- }
-
- private enum Shape {
- ONE_BY_ONE("1x1"),
- ONE_BY_TWO("1x2"),
- ONE_BY_THREE("1x3"),
- ONE_BY_FOUR("1x4"),
- L_SHAPE("L-shape"),
- TWO_BY_TWO("2x2");
- final String shape;
-
- Shape(String shape) {
- this.shape = shape;
- }
-
- @Override
- public String toString() {
- return shape;
- }
- }
-
- public enum Direction {
- NW, NE, SW, SE
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
deleted file mode 100644
index 73ac1883..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets;
-
-import com.google.gson.JsonObject;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfig;
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.render.RenderHelper;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.entity.Entity;
-import net.minecraft.text.Text;
-import net.minecraft.util.Formatting;
-import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Vec3d;
-
-import java.util.List;
-import java.util.function.Predicate;
-import java.util.function.ToDoubleFunction;
-
-public class SecretWaypoint {
- static final List<String> SECRET_ITEMS = List.of("Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "Treasure Talisman");
- final int secretIndex;
- final Category category;
- private final Text name;
- private final BlockPos pos;
- private final Vec3d centerPos;
- private boolean missing;
-
- SecretWaypoint(int secretIndex, JsonObject waypoint, String name, BlockPos pos) {
- this.secretIndex = secretIndex;
- this.category = Category.get(waypoint);
- this.name = Text.of(name);
- this.pos = pos;
- this.centerPos = pos.toCenterPos();
- this.missing = true;
- }
-
- static ToDoubleFunction<SecretWaypoint> getSquaredDistanceToFunction(Entity entity) {
- return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos);
- }
-
- static Predicate<SecretWaypoint> getRangePredicate(Entity entity) {
- return secretWaypoint -> entity.squaredDistanceTo(secretWaypoint.centerPos) <= 36D;
- }
-
- boolean shouldRender() {
- return category.isEnabled() && missing;
- }
-
- boolean needsInteraction() {
- return category.needsInteraction();
- }
-
- boolean isLever() {
- return category.isLever();
- }
-
- boolean needsItemPickup() {
- return category.needsItemPickup();
- }
-
- boolean isBat() {
- return category.isBat();
- }
-
- void setFound() {
- this.missing = false;
- }
-
- void setMissing() {
- this.missing = true;
- }
-
- /**
- * Renders the secret waypoint, including a filled cube, a beacon beam, the name, and the distance from the player.
- */
- void render(WorldRenderContext context) {
- RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, pos, category.colorComponents, 0.5F);
- Vec3d posUp = centerPos.add(0, 1, 0);
- RenderHelper.renderText(context, name, posUp, true);
- double distance = context.camera().getPos().distanceTo(centerPos);
- RenderHelper.renderText(context, Text.literal(Math.round(distance) + "m").formatted(Formatting.YELLOW), posUp, 1, MinecraftClient.getInstance().textRenderer.fontHeight + 1, true);
- }
-
- enum Category {
- ENTRANCE(secretWaypoints -> secretWaypoints.enableEntranceWaypoints, 0, 255, 0),
- SUPERBOOM(secretWaypoints -> secretWaypoints.enableSuperboomWaypoints, 255, 0, 0),
- CHEST(secretWaypoints -> secretWaypoints.enableChestWaypoints, 2, 213, 250),
- ITEM(secretWaypoints -> secretWaypoints.enableItemWaypoints, 2, 64, 250),
- BAT(secretWaypoints -> secretWaypoints.enableBatWaypoints, 142, 66, 0),
- WITHER(secretWaypoints -> secretWaypoints.enableWitherWaypoints, 30, 30, 30),
- LEVER(secretWaypoints -> secretWaypoints.enableLeverWaypoints, 250, 217, 2),
- FAIRYSOUL(secretWaypoints -> secretWaypoints.enableFairySoulWaypoints, 255, 85, 255),
- STONK(secretWaypoints -> secretWaypoints.enableStonkWaypoints, 146, 52, 235),
- DEFAULT(secretWaypoints -> secretWaypoints.enableDefaultWaypoints, 190, 255, 252);
- private final Predicate<SkyblockerConfig.SecretWaypoints> enabledPredicate;
- private final float[] colorComponents;
-
- Category(Predicate<SkyblockerConfig.SecretWaypoints> enabledPredicate, int... intColorComponents) {
- this.enabledPredicate = enabledPredicate;
- colorComponents = new float[intColorComponents.length];
- for (int i = 0; i < intColorComponents.length; i++) {
- colorComponents[i] = intColorComponents[i] / 255F;
- }
- }
-
- private static Category get(JsonObject categoryJson) {
- return switch (categoryJson.get("category").getAsString()) {
- case "entrance" -> Category.ENTRANCE;
- case "superboom" -> Category.SUPERBOOM;
- case "chest" -> Category.CHEST;
- case "item" -> Category.ITEM;
- case "bat" -> Category.BAT;
- case "wither" -> Category.WITHER;
- case "lever" -> Category.LEVER;
- case "fairysoul" -> Category.FAIRYSOUL;
- case "stonk" -> Category.STONK;
- default -> Category.DEFAULT;
- };
- }
-
- boolean needsInteraction() {
- return this == CHEST || this == WITHER;
- }
-
- boolean isLever() {
- return this == LEVER;
- }
-
- boolean needsItemPickup() {
- return this == ITEM;
- }
-
- boolean isBat() {
- return this == BAT;
- }
-
- boolean isEnabled() {
- return enabledPredicate.test(SkyblockerConfigManager.get().locations.dungeons.secretWaypoints);
- }
- }
-}
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java
deleted file mode 100644
index 787d696e..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/ColorTerminal.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight;
-import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver;
-import net.minecraft.item.Item;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-import net.minecraft.registry.Registries;
-import net.minecraft.util.DyeColor;
-import net.minecraft.util.Identifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
-
-
-public class ColorTerminal extends ContainerSolver {
- private static final Logger LOGGER = LoggerFactory.getLogger(ColorTerminal.class.getName());
- private static final Map<String, DyeColor> colorFromName;
- private DyeColor targetColor;
- private static final Map<Item, DyeColor> itemColor;
-
- public ColorTerminal() {
- super("^Select all the ([A-Z ]+) items!$");
- }
-
- @Override
- protected boolean isEnabled() {
- targetColor = null;
- return SkyblockerConfigManager.get().locations.dungeons.terminals.solveColor;
- }
-
- @Override
- protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
- trimEdges(slots, 6);
- List<ColorHighlight> highlights = new ArrayList<>();
- String colorString = groups[0];
- if (targetColor == null) {
- targetColor = colorFromName.get(colorString);
- if (targetColor == null) {
- LOGGER.error("[Skyblocker] Couldn't find dye color corresponding to \"" + colorString + "\"");
- return Collections.emptyList();
- }
- }
- for (Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
- ItemStack itemStack = slot.getValue();
- if (!itemStack.hasEnchantments() && targetColor.equals(itemColor.get(itemStack.getItem()))) {
- highlights.add(ColorHighlight.green(slot.getKey()));
- }
- }
- return highlights;
- }
-
-
- static {
- colorFromName = new HashMap<>();
- for (DyeColor color : DyeColor.values())
- colorFromName.put(color.getName().toUpperCase(Locale.ENGLISH), color);
- colorFromName.put("SILVER", DyeColor.LIGHT_GRAY);
- colorFromName.put("LIGHT BLUE", DyeColor.LIGHT_BLUE);
-
- itemColor = new HashMap<>();
- for (DyeColor color : DyeColor.values())
- for (String item : new String[]{"dye", "wool", "stained_glass", "terracotta"})
- itemColor.put(Registries.ITEM.get(new Identifier(color.getName() + '_' + item)), color);
- itemColor.put(Items.BONE_MEAL, DyeColor.WHITE);
- itemColor.put(Items.LAPIS_LAZULI, DyeColor.BLUE);
- itemColor.put(Items.COCOA_BEANS, DyeColor.BROWN);
- itemColor.put(Items.INK_SAC, DyeColor.BLACK);
- }
-} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java
deleted file mode 100644
index afb07b4b..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/OrderTerminal.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight;
-import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver;
-import net.minecraft.item.ItemStack;
-import net.minecraft.item.Items;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-public class OrderTerminal extends ContainerSolver {
- private final int PANES_NUM = 14;
- private int[] orderedSlots;
- private int currentNum = Integer.MAX_VALUE;
-
- public OrderTerminal() {
- super("^Click in order!$");
- }
-
- @Override
- protected boolean isEnabled() {
- orderedSlots = null;
- currentNum = 0;
- return SkyblockerConfigManager.get().locations.dungeons.terminals.solveOrder;
- }
-
- @Override
- protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
- if(orderedSlots == null && !orderSlots(slots))
- return Collections.emptyList();
- while(currentNum < PANES_NUM && Items.LIME_STAINED_GLASS_PANE.equals(slots.get(orderedSlots[currentNum]).getItem()))
- currentNum++;
- List<ColorHighlight> highlights = new ArrayList<>(3);
- int last = Integer.min(3, PANES_NUM - currentNum);
- for(int i = 0; i < last; i++) {
- highlights.add(new ColorHighlight(orderedSlots[currentNum + i], (224 - 64 * i) << 24 | 64 << 16 | 96 << 8 | 255));
- }
- return highlights;
- }
-
- public boolean orderSlots(Map<Integer, ItemStack> slots) {
- trimEdges(slots, 4);
- orderedSlots = new int[PANES_NUM];
- for(Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
- if(Items.AIR.equals(slot.getValue().getItem())) {
- orderedSlots = null;
- return false;
- }
- else
- orderedSlots[slot.getValue().getCount() - 1] = slot.getKey();
- }
- currentNum = 0;
- return true;
- }
-} \ No newline at end of file
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java
deleted file mode 100644
index fa9fa324..00000000
--- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/terminal/StartsWithTerminal.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package me.xmrvizzy.skyblocker.skyblock.dungeon.terminal;
-
-import me.xmrvizzy.skyblocker.config.SkyblockerConfigManager;
-import me.xmrvizzy.skyblocker.utils.render.gui.ColorHighlight;
-import me.xmrvizzy.skyblocker.utils.render.gui.ContainerSolver;
-import net.minecraft.item.ItemStack;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-public class StartsWithTerminal extends ContainerSolver {
- public StartsWithTerminal() {
- super("^What starts with: '([A-Z])'\\?$");
- }
-
- @Override
- protected boolean isEnabled() {
- return SkyblockerConfigManager.get().locations.dungeons.terminals.solveStartsWith;
- }
-
- @Override
- protected List<ColorHighlight> getColors(String[] groups, Map<Integer, ItemStack> slots) {
- trimEdges(slots, 6);
- String prefix = groups[0];
- List<ColorHighlight> highlights = new ArrayList<>();
- for (Map.Entry<Integer, ItemStack> slot : slots.entrySet()) {
- ItemStack stack = slot.getValue();
- if (!stack.hasEnchantments() && stack.getName().getString().startsWith(prefix)) {
- highlights.add(ColorHighlight.green(slot.getKey()));
- }
- }
- return highlights;
- }
-} \ No newline at end of file