aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java10
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java170
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java181
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java71
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java19
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java24
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java)2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java)14
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java18
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java87
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Http.java14
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Location.java116
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Utils.java80
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json8
-rw-r--r--src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json17
-rw-r--r--src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json17
-rw-r--r--src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java29
-rw-r--r--src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java40
-rw-r--r--src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java (renamed from src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java)2
27 files changed, 859 insertions, 109 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index fc72ea3f..edd4241e 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -10,25 +10,23 @@ import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra;
import de.hysky.skyblocker.skyblock.chat.ChatRulesHandler;
import de.hysky.skyblocker.skyblock.dungeon.*;
import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
+import de.hysky.skyblocker.skyblock.dungeon.puzzle.*;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.boulder.Boulder;
-import de.hysky.skyblocker.skyblock.dungeon.puzzle.CreeperBeams;
-import de.hysky.skyblocker.skyblock.dungeon.puzzle.DungeonBlaze;
-import de.hysky.skyblocker.skyblock.dungeon.puzzle.TicTacToe;
import de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard.Waterboard;
import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
import de.hysky.skyblocker.skyblock.dungeon.secrets.SecretsTracker;
import de.hysky.skyblocker.skyblock.dwarven.CrystalsHud;
import de.hysky.skyblocker.skyblock.dwarven.CrystalsLocationsManager;
import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud;
-import de.hysky.skyblocker.skyblock.end.TheEnd;
import de.hysky.skyblocker.skyblock.end.BeaconHighlighter;
+import de.hysky.skyblocker.skyblock.end.TheEnd;
import de.hysky.skyblocker.skyblock.item.*;
import de.hysky.skyblocker.skyblock.item.tooltip.BackpackPreview;
import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
import de.hysky.skyblocker.skyblock.quicknav.QuickNav;
import de.hysky.skyblocker.skyblock.rift.TheRift;
-import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager;
+import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager;
import de.hysky.skyblocker.skyblock.shortcut.Shortcuts;
import de.hysky.skyblocker.skyblock.special.SpecialEffects;
import de.hysky.skyblocker.skyblock.tabhud.TabHud;
@@ -121,6 +119,8 @@ public class SkyblockerMod implements ClientModInitializer {
DungeonManager.init();
DungeonBlaze.init();
Waterboard.init();
+ Silverfish.init();
+ IceFill.init();
DungeonScore.init();
PartyFinderScreen.initClass();
ChestValue.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index ea1f7d43..4a1fe614 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -723,6 +723,12 @@ public class SkyblockerConfig {
public boolean solveBoulder = true;
@SerialEntry
+ public boolean solveIceFill = true;
+
+ @SerialEntry
+ public boolean solveSilverfish = true;
+
+ @SerialEntry
public boolean fireFreezeStaffTimer = true;
@SerialEntry
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
index 5eb9a066..3ebd5d76 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
@@ -417,6 +417,20 @@ public class DungeonsCategory {
.controller(ConfigUtils::createBooleanController)
.build())
.option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill"))
+ .binding(defaults.locations.dungeons.solveIceFill,
+ () -> config.locations.dungeons.solveIceFill,
+ newValue -> config.locations.dungeons.solveIceFill = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish"))
+ .binding(defaults.locations.dungeons.solveSilverfish,
+ () -> config.locations.dungeons.solveSilverfish,
+ newValue -> config.locations.dungeons.solveSilverfish = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
.name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer"))
.description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.fireFreezeStaffTimer.@Tooltip")))
.binding(defaults.locations.dungeons.fireFreezeStaffTimer,
diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
index 2a4c38a7..ceda9ed4 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
@@ -6,8 +6,8 @@ import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
import de.hysky.skyblocker.skyblock.item.HotbarSlotLock;
import de.hysky.skyblocker.skyblock.item.ItemProtection;
import de.hysky.skyblocker.skyblock.rift.HealingMelonIndicator;
-import de.hysky.skyblocker.skyblock.searchOverlay.OverlayScreen;
-import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager;
+import de.hysky.skyblocker.skyblock.searchoverlay.OverlayScreen;
+import de.hysky.skyblocker.skyblock.searchoverlay.SearchOverManager;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.client.MinecraftClient;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java
index 033a919d..5bc98894 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/crimson/kuudra/Kuudra.java
@@ -10,7 +10,6 @@ import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
public class Kuudra {
- public static final String LOCATION = "kuudra";
static KuudraPhase phase = KuudraPhase.OTHER;
public static void init() {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java
new file mode 100644
index 00000000..57386674
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFill.java
@@ -0,0 +1,170 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import com.google.common.primitives.Booleans;
+import com.mojang.brigadier.Command;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.debug.Debug;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.Room;
+import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.world.World;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class IceFill extends DungeonPuzzle {
+ public static final IceFill INSTANCE = new IceFill();
+ private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents();
+ private static final BlockPos[] BOARD_ORIGINS = {
+ new BlockPos(16, 70, 9),
+ new BlockPos(17, 71, 16),
+ new BlockPos(18, 72, 25)
+ };
+ private CompletableFuture<Void> solve;
+ private final boolean[][][] iceFillBoards = {new boolean[3][3], new boolean[5][5], new boolean[7][7]};
+ private final List<List<Vector2ic>> iceFillPaths = List.of(new ArrayList<>(), new ArrayList<>(), new ArrayList<>());
+
+ private IceFill() {
+ super("ice-fill", "ice-path");
+ }
+
+ public static void init() {
+ if (Debug.debugEnabled()) {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName)
+ .then(literal("printBoard1").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[0])));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printBoard2").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[1])));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printBoard3").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.iceFillBoards[2])));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath1").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(0).toString()));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath2").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(1).toString()));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath3").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.iceFillPaths.get(2).toString()));
+ return Command.SINGLE_SUCCESS;
+ }))
+ )))));
+ }
+ }
+
+ private static String boardToString(boolean[][] iceFillBoard) {
+ StringBuilder sb = new StringBuilder();
+ for (boolean[] row : iceFillBoard) {
+ sb.append("\n");
+ for (boolean cell : row) {
+ sb.append(cell ? '#' : '.');
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void tick(MinecraftClient client) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill || client.world == null || !DungeonManager.isCurrentRoomMatched() || solve != null && !solve.isDone()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+
+ solve = CompletableFuture.runAsync(() -> {
+ BlockPos.Mutable pos = new BlockPos.Mutable();
+ for (int i = 0; i < 3; i++) {
+ if (updateBoard(client.world, room, iceFillBoards[i], pos.set(BOARD_ORIGINS[i]))) {
+ solve(iceFillBoards[i], iceFillPaths.get(i));
+ }
+ }
+ });
+ }
+
+ private boolean updateBoard(World world, Room room, boolean[][] iceFillBoard, BlockPos.Mutable pos) {
+ boolean boardChanged = false;
+ for (int row = 0; row < iceFillBoard.length; pos.move(iceFillBoard[row].length, 0, -1), row++) {
+ for (int col = 0; col < iceFillBoard[row].length; pos.move(Direction.WEST), col++) {
+ BlockPos actualPos = room.relativeToActual(pos);
+ boolean isBlock = !world.getBlockState(actualPos).isAir();
+ if (iceFillBoard[row][col] != isBlock) {
+ iceFillBoard[row][col] = isBlock;
+ boardChanged = true;
+ }
+ }
+ }
+ return boardChanged;
+ }
+
+ void solve(boolean[][] iceFillBoard, List<Vector2ic> iceFillPath) {
+ Vector2ic start = new Vector2i(iceFillBoard.length - 1, iceFillBoard[0].length / 2);
+ int count = iceFillBoard.length * iceFillBoard[0].length - Arrays.stream(iceFillBoard).mapToInt(Booleans::countTrue).sum();
+
+ List<Vector2ic> newPath = solveDfs(iceFillBoard, count - 1, new ArrayList<>(List.of(start)), new HashSet<>(List.of(start)));
+ if (newPath != null) {
+ iceFillPath.clear();
+ iceFillPath.addAll(newPath);
+ }
+ }
+
+ private List<Vector2ic> solveDfs(boolean[][] iceFillBoard, int count, List<Vector2ic> path, Set<Vector2ic> visited) {
+ Vector2ic pos = path.get(path.size() - 1);
+ if (count == 0) {
+ if (pos.x() == 0 && pos.y() == iceFillBoard[0].length / 2) {
+ return path;
+ } else {
+ return null;
+ }
+ }
+
+ Vector2ic[] newPosArray = {pos.add(1, 0, new Vector2i()), pos.add(-1, 0, new Vector2i()), pos.add(0, 1, new Vector2i()), pos.add(0, -1, new Vector2i())};
+ for (Vector2ic newPos : newPosArray) {
+ if (newPos.x() >= 0 && newPos.x() < iceFillBoard.length && newPos.y() >= 0 && newPos.y() < iceFillBoard[0].length && !iceFillBoard[newPos.x()][newPos.y()] && !visited.contains(newPos)) {
+ path.add(newPos);
+ visited.add(newPos);
+ List<Vector2ic> newPath = solveDfs(iceFillBoard, count - 1, path, visited);
+ if (newPath != null) {
+ return newPath;
+ }
+ path.remove(path.size() - 1);
+ visited.remove(newPos);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public void render(WorldRenderContext context) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveIceFill || !DungeonManager.isCurrentRoomMatched()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+ for (int i = 0; i < 3; i++) {
+ renderPath(context, room, iceFillPaths.get(i), BOARD_ORIGINS[i]);
+ }
+ }
+
+ private void renderPath(WorldRenderContext context, Room room, List<Vector2ic> iceFillPath, BlockPos originPos) {
+ BlockPos.Mutable pos = new BlockPos.Mutable();
+ for (int i = 0; i < iceFillPath.size() - 1; i++) {
+ Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(originPos).move(-iceFillPath.get(i).y(), 0, -iceFillPath.get(i).x())));
+ Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(originPos).move(-iceFillPath.get(i + 1).y(), 0, -iceFillPath.get(i + 1).x())));
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, RED_COLOR_COMPONENTS, 1f, 5f, true);
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java
new file mode 100644
index 00000000..b5cbc8ee
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Silverfish.java
@@ -0,0 +1,181 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import com.mojang.brigadier.Command;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.debug.Debug;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.Room;
+import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.mob.SilverfishEntity;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Box;
+import net.minecraft.util.math.Direction;
+import net.minecraft.util.math.Vec3d;
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class Silverfish extends DungeonPuzzle {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Silverfish.class);
+ public static final Silverfish INSTANCE = new Silverfish();
+ private static final float[] RED_COLOR_COMPONENTS = DyeColor.RED.getColorComponents();
+ final boolean[][] silverfishBoard = new boolean[17][17];
+ Vector2ic silverfishPos;
+ final List<Vector2ic> silverfishPath = new ArrayList<>();
+
+ private Silverfish() {
+ super("silverfish", "ice-silverfish-room");
+ }
+
+ public static void init() {
+ if (Debug.debugEnabled()) {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName)
+ .then(literal("printBoard").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.silverfishBoard)));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printPath").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.silverfishPath.toString()));
+ return Command.SINGLE_SUCCESS;
+ }))
+ )))));
+ }
+ }
+
+ private static String boardToString(boolean[][] silverfishBoard) {
+ StringBuilder sb = new StringBuilder();
+ for (boolean[] row : silverfishBoard) {
+ sb.append("\n");
+ for (boolean cell : row) {
+ sb.append(cell ? '#' : '.');
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void tick(MinecraftClient client) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveSilverfish || client.world == null || !DungeonManager.isCurrentRoomMatched()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+
+ boolean boardChanged = false;
+ BlockPos.Mutable pos = new BlockPos.Mutable(23, 67, 24);
+ for (int row = 0; row < silverfishBoard.length; pos.move(silverfishBoard[row].length, 0, -1), row++) {
+ for (int col = 0; col < silverfishBoard[row].length; pos.move(Direction.WEST), col++) {
+ boolean isBlock = !client.world.getBlockState(room.relativeToActual(pos)).isAir();
+ if (silverfishBoard[row][col] != isBlock) {
+ silverfishBoard[row][col] = isBlock;
+ boardChanged = true;
+ }
+ }
+ }
+
+ List<SilverfishEntity> entities = client.world.getEntitiesByClass(SilverfishEntity.class, Box.of(Vec3d.ofCenter(room.relativeToActual(new BlockPos(15, 66, 16))), 16, 16, 16), silverfishEntity -> true);
+ if (entities.isEmpty()) {
+ return;
+ }
+ BlockPos newSilverfishBlockPos = room.actualToRelative(entities.get(0).getBlockPos());
+ Vector2ic newSilverfishPos = new Vector2i(24 - newSilverfishBlockPos.getZ(), 23 - newSilverfishBlockPos.getX());
+ if (newSilverfishPos.x() < 0 || newSilverfishPos.x() >= 17 || newSilverfishPos.y() < 0 || newSilverfishPos.y() >= 17) {
+ return;
+ }
+ boolean silverfishChanged = !newSilverfishPos.equals(silverfishPos);
+ if (silverfishChanged) {
+ silverfishPos = newSilverfishPos;
+ }
+ if (silverfishChanged || boardChanged) {
+ solve();
+ }
+ }
+
+ void solve() {
+ if (silverfishPos == null) {
+ return;
+ }
+ Set<Vector2ic> visited = new HashSet<>();
+ Queue<List<Vector2ic>> queue = new ArrayDeque<>();
+ queue.add(List.of(silverfishPos));
+ visited.add(silverfishPos);
+ while (!queue.isEmpty()) {
+ List<Vector2ic> path = queue.poll();
+ Vector2ic pos = path.get(path.size() - 1);
+ if (pos.x() == 0 && pos.y() >= 7 && pos.y() <= 9) {
+ silverfishPath.clear();
+ silverfishPath.addAll(path);
+ return;
+ }
+
+ Vector2i posMutable = new Vector2i(pos);
+ while (posMutable.x() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(1, 0);
+ }
+ posMutable.add(-1, 0);
+ addQueue(visited, queue, path, posMutable);
+
+ posMutable = new Vector2i(pos);
+ while (posMutable.x() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(-1, 0);
+ }
+ posMutable.add(1, 0);
+ addQueue(visited, queue, path, posMutable);
+
+ posMutable = new Vector2i(pos);
+ while (posMutable.y() < 17 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(0, 1);
+ }
+ posMutable.add(0, -1);
+ addQueue(visited, queue, path, posMutable);
+
+ posMutable = new Vector2i(pos);
+ while (posMutable.y() >= 0 && !silverfishBoard[posMutable.x()][posMutable.y()]) {
+ posMutable.add(0, -1);
+ }
+ posMutable.add(0, 1);
+ addQueue(visited, queue, path, posMutable);
+ }
+ }
+
+ private void addQueue(Set<Vector2ic> visited, Queue<List<Vector2ic>> queue, List<Vector2ic> path, Vector2ic newPos) {
+ if (!visited.contains(newPos)) {
+ List<Vector2ic> newPath = new ArrayList<>(path);
+ newPath.add(newPos);
+ queue.add(newPath);
+ visited.add(newPos);
+ }
+ }
+
+ @Override
+ public void render(WorldRenderContext context) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.solveSilverfish || !DungeonManager.isCurrentRoomMatched() || silverfishPath.isEmpty()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+ BlockPos.Mutable pos = new BlockPos.Mutable();
+ for (int i = 0; i < silverfishPath.size() - 1; i++) {
+ Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i).y(), 67, 24 - silverfishPath.get(i).x())));
+ Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.set(23 - silverfishPath.get(i + 1).y(), 67, 24 - silverfishPath.get(i + 1).x())));
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, end}, RED_COLOR_COMPONENTS, 1f, 5f, true);
+ }
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ for (boolean[] silverfishBoardRow : silverfishBoard) {
+ Arrays.fill(silverfishBoardRow, false);
+ }
+ silverfishPos = null;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
index 3244996a..ba4b9a5f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
@@ -88,17 +88,20 @@ public class Waterboard extends DungeonPuzzle {
private Waterboard() {
super("waterboard", "water-puzzle");
- UseBlockCallback.EVENT.register(this::onUseBlock);
+ }
+
+ public static void init() {
+ UseBlockCallback.EVENT.register(INSTANCE::onUseBlock);
if (Debug.debugEnabled()) {
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName)
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(INSTANCE.puzzleName)
.then(literal("printBoard").executes(context -> {
- context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(cells)));
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(INSTANCE.cells)));
return Command.SINGLE_SUCCESS;
})).then(literal("printDoors").executes(context -> {
context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.doors)));
return Command.SINGLE_SUCCESS;
})).then(literal("printSimulationResults").then(argument("combination", IntegerArgumentType.integer(0, 63)).executes(context -> {
- context.getSource().sendFeedback(Constants.PREFIX.get().append(results[IntegerArgumentType.getInteger(context, "combination")].toString()));
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.results[IntegerArgumentType.getInteger(context, "combination")].toString()));
return Command.SINGLE_SUCCESS;
}))).then(literal("printCurrentCombination").executes(context -> {
context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.currentCombination)));
@@ -111,9 +114,6 @@ public class Waterboard extends DungeonPuzzle {
}
}
- public static void init() {
- }
-
private static String boardToString(Cell[][] cells) {
StringBuilder sb = new StringBuilder();
for (Cell[] row : cells) {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java
index 68f09344..2f748792 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java
@@ -10,9 +10,6 @@ import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.Http;
import de.hysky.skyblocker.utils.Http.ApiResponse;
import de.hysky.skyblocker.utils.Utils;
-import it.unimi.dsi.fastutil.ints.IntIntPair;
-import it.unimi.dsi.fastutil.objects.Object2IntMap.Entry;
-import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.minecraft.client.MinecraftClient;
@@ -24,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -43,8 +41,6 @@ public class SecretsTracker {
ClientReceiveMessageEvents.GAME.register(SecretsTracker::onMessage);
}
- //If -1 is somehow encountered, it would be very rare, so I just disregard its possibility for now
- //people would probably recognize if it was inaccurate so yeah
private static void calculate(RunPhase phase) {
switch (phase) {
case START -> CompletableFuture.runAsync(() -> {
@@ -57,11 +53,11 @@ public class SecretsTracker {
//The player name will be blank if there isn't a player at that index
if (!playerName.isEmpty()) {
- //If the player was a part of the last run (and didn't have -1 secret count) and that run ended less than 5 mins ago then copy the secrets over
- if (lastRun != null && System.currentTimeMillis() <= lastRunEnded + 300_000 && lastRun.secretCounts().getOrDefault(playerName, -1) != -1) {
- newlyStartedRun.secretCounts().put(playerName, lastRun.secretCounts().getInt(playerName));
+ //If the player was a part of the last run, had non-empty secret data and that run ended less than 5 mins ago then copy the secret data over
+ if (lastRun != null && System.currentTimeMillis() <= lastRunEnded + 300_000 && lastRun.playersSecretData().getOrDefault(playerName, SecretData.EMPTY) != SecretData.EMPTY) {
+ newlyStartedRun.playersSecretData().put(playerName, lastRun.playersSecretData().get(playerName));
} else {
- newlyStartedRun.secretCounts().put(playerName, getPlayerSecrets(playerName).leftInt());
+ newlyStartedRun.playersSecretData().put(playerName, getPlayerSecrets(playerName));
}
}
}
@@ -72,22 +68,23 @@ public class SecretsTracker {
case END -> CompletableFuture.runAsync(() -> {
//In case the game crashes from something
if (currentRun != null) {
- Object2ObjectOpenHashMap<String, IntIntPair> secretsFound = new Object2ObjectOpenHashMap<>();
+ Object2ObjectOpenHashMap<String, SecretData> secretsFound = new Object2ObjectOpenHashMap<>();
//Update secret counts
- for (Entry<String> entry : currentRun.secretCounts().object2IntEntrySet()) {
+ for (Entry<String, SecretData> entry : currentRun.playersSecretData().entrySet()) {
String playerName = entry.getKey();
- int startingSecrets = entry.getIntValue();
- IntIntPair secretsNow = getPlayerSecrets(playerName);
- int secretsPlayerFound = secretsNow.leftInt() - startingSecrets;
+ SecretData startingSecrets = entry.getValue();
+ SecretData secretsNow = getPlayerSecrets(playerName);
+ int secretsPlayerFound = secretsNow.secrets() - startingSecrets.secrets();
- secretsFound.put(playerName, IntIntPair.of(secretsPlayerFound, secretsNow.rightInt()));
- entry.setValue(secretsNow.leftInt());
+ //Add an entry to the secretsFound map with the data - if the secret data from now or the start was cached a warning will be shown
+ secretsFound.put(playerName, secretsNow.updated(secretsPlayerFound, startingSecrets.cached() || secretsNow.cached()));
+ entry.setValue(secretsNow);
}
//Print the results all in one go, so its clean and less of a chance of it being broken up
- for (Map.Entry<String, IntIntPair> entry : secretsFound.entrySet()) {
- sendResultMessage(entry.getKey(), entry.getValue().leftInt(), entry.getValue().rightInt(), true);
+ for (Map.Entry<String, SecretData> entry : secretsFound.entrySet()) {
+ sendResultMessage(entry.getKey(), entry.getValue(), true);
}
//Swap the current and last run as well as mark the run end time
@@ -95,30 +92,31 @@ public class SecretsTracker {
lastRun = currentRun;
currentRun = null;
} else {
- sendResultMessage(null, -1, -1, false);
+ sendResultMessage(null, null, false);
}
});
}
}
- private static void sendResultMessage(String player, int secrets, int cacheAge, boolean success) {
+ private static void sendResultMessage(String player, SecretData secretData, boolean success) {
+ @SuppressWarnings("resource")
PlayerEntity playerEntity = MinecraftClient.getInstance().player;
if (playerEntity != null) {
if (success) {
- playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.feedback", Text.literal(player).withColor(0xf57542), "§7" + secrets, getCacheText(cacheAge))));
+ playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.feedback", Text.literal(player).withColor(0xf57542), "§7" + secretData.secrets(), getCacheText(secretData.cached(), secretData.cacheAge()))));
} else {
playerEntity.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secretsTracker.failFeedback")));
}
}
}
- private static Text getCacheText(int cacheAge) {
- return Text.literal("\u2139").styled(style -> style.withColor(cacheAge == -1 ? 0x218bff : 0xeac864).withHoverEvent(
- new HoverEvent(HoverEvent.Action.SHOW_TEXT, cacheAge == -1 ? Text.translatable("skyblocker.api.cache.MISS") : Text.translatable("skyblocker.api.cache.HIT", cacheAge))));
+ private static Text getCacheText(boolean cached, int cacheAge) {
+ return Text.literal("\u2139").styled(style -> style.withColor(cached ? 0xeac864 : 0x218bff).withHoverEvent(
+ new HoverEvent(HoverEvent.Action.SHOW_TEXT, cached ? Text.translatable("skyblocker.api.cache.HIT", cacheAge) : Text.translatable("skyblocker.api.cache.MISS"))));
}
private static void onMessage(Text text, boolean overlay) {
- if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.playerSecretsTracker) {
+ if (Utils.isInDungeons() && SkyblockerConfigManager.get().locations.dungeons.playerSecretsTracker && !overlay) {
String message = Formatting.strip(text.getString());
try {
@@ -136,35 +134,44 @@ public class SecretsTracker {
return matcher != null ? matcher.group("name") : "";
}
- private static IntIntPair getPlayerSecrets(String name) {
+ private static SecretData getPlayerSecrets(String name) {
String uuid = ApiUtils.name2Uuid(name);
if (!uuid.isEmpty()) {
try (ApiResponse response = Http.sendHypixelRequest("player", "?uuid=" + uuid)) {
- return IntIntPair.of(getSecretCountFromAchievements(JsonParser.parseString(response.content()).getAsJsonObject()), response.age());
+ return new SecretData(getSecretCountFromAchievements(JsonParser.parseString(response.content()).getAsJsonObject()), response.cached(), response.age());
} catch (Exception e) {
LOGGER.error("[Skyblocker] Encountered an error while trying to fetch {} secret count!", name + "'s", e);
}
}
- return IntIntPair.of(-1, -1);
+ return SecretData.EMPTY;
}
/**
* Gets a player's secret count from their hypixel achievements
*/
private static int getSecretCountFromAchievements(JsonObject playerJson) {
- JsonObject player = playerJson.get("player").getAsJsonObject();
- JsonObject achievements = (player.has("achievements")) ? player.get("achievements").getAsJsonObject() : null;
+ JsonObject player = playerJson.getAsJsonObject("player");
+ JsonObject achievements = player.has("achievements") ? player.getAsJsonObject("achievements") : null;
return (achievements != null && achievements.has("skyblock_treasure_hunter")) ? achievements.get("skyblock_treasure_hunter").getAsInt() : 0;
}
/**
* This will either reflect the value at the start or the end depending on when this is called
*/
- private record TrackedRun(Object2IntOpenHashMap<String> secretCounts) {
+ private record TrackedRun(Object2ObjectOpenHashMap<String, SecretData> playersSecretData) {
private TrackedRun() {
- this(new Object2IntOpenHashMap<>());
+ this(new Object2ObjectOpenHashMap<>());
+ }
+ }
+
+ private record SecretData(int secrets, boolean cached, int cacheAge) {
+ private static final SecretData EMPTY = new SecretData(0, false, 0);
+
+ //If only we had Derived Record Creation :( - https://bugs.openjdk.org/browse/JDK-8321133
+ private SecretData updated(int secrets, boolean cached) {
+ return new SecretData(secrets, cached, this.cacheAge);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java
index 5913a3c6..608873c0 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dwarven/DwarvenHud.java
@@ -203,8 +203,8 @@ public class DwarvenHud {
}
public static void update() {
- if (client.player == null || client.getNetworkHandler() == null || !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions || (!Utils.isInCrystalHollows()
- && !Utils.isInDwarvenMines()))
+ if (client.player == null || client.getNetworkHandler() == null || (!SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledCommissions && !SkyblockerConfigManager.get().locations.dwarvenMines.dwarvenHud.enabledPowder)
+ || (!Utils.isInCrystalHollows() && !Utils.isInDwarvenMines()))
return;
commissionList = new ArrayList<>();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java b/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java
index f69fca6d..6c89a07c 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/end/BeaconHighlighter.java
@@ -3,8 +3,11 @@ package de.hysky.skyblocker.skyblock.end;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.render.RenderHelper;
+import net.fabricmc.fabric.api.client.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.minecraft.text.Text;
import net.minecraft.util.math.BlockPos;
import java.util.ArrayList;
@@ -20,6 +23,20 @@ public class BeaconHighlighter {
*/
public static void init() {
WorldRenderEvents.AFTER_TRANSLUCENT.register(BeaconHighlighter::render);
+ ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset());
+ ClientReceiveMessageEvents.GAME.register(BeaconHighlighter::onMessage);
+ }
+
+ private static void reset() {
+ beaconPositions.clear();
+ }
+
+ private static void onMessage(Text text, boolean overlay) {
+ if (Utils.isInTheEnd() && !overlay) {
+ String message = text.getString();
+
+ if (message.contains("SLAYER QUEST COMPLETE!") || message.contains("NICE! SLAYER BOSS SLAIN!")) reset();
+ }
}
/**
@@ -28,7 +45,7 @@ public class BeaconHighlighter {
*
* @param context An instance of WorldRenderContext for the RenderHelper to use
*/
- public static void render(WorldRenderContext context) {
+ private static void render(WorldRenderContext context) {
if (Utils.isInTheEnd() && SkyblockerConfigManager.get().slayer.endermanSlayer.highlightBeacons) {
for (BlockPos pos : beaconPositions) {
RenderHelper.renderFilled(context, pos, RED_COLOR_COMPONENTS, 0.5f, false);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
index 19f2e6fd..d5be7eee 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
@@ -140,15 +140,37 @@ public class ItemTooltip {
}
}
+ final Map<Integer, String> itemTierFloors = new HashMap<>() {{
+ put(1, "F1");
+ put(2, "F2");
+ put(3, "F3");
+ put(4, "F4/M1");
+ put(5, "F5/M2");
+ put(6, "F6/M3");
+ put(7, "F7/M4");
+ put(8, "M5");
+ put(9, "M6");
+ put(10, "M7");
+ }};
+
if (SkyblockerConfigManager.get().general.dungeonQuality) {
NbtCompound ea = ItemUtils.getExtraAttributes(stack);
if (ea != null && ea.contains("baseStatBoostPercentage")) {
int baseStatBoostPercentage = ea.getInt("baseStatBoostPercentage");
- if (baseStatBoostPercentage == 50) {
+ boolean maxQuality = baseStatBoostPercentage == 50;
+ if (maxQuality) {
lines.add(Text.literal(String.format("%-17s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.RED).formatted(Formatting.BOLD));
} else {
lines.add(Text.literal(String.format("%-21s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.BLUE));
}
+ if (ea.contains("item_tier")) { // sometimes it just isn't here?
+ int itemTier = ea.getInt("item_tier");
+ if (maxQuality) {
+ lines.add(Text.literal(String.format("%-17s", "Floor Tier:") + itemTier + " (" + itemTierFloors.get(itemTier) + ")").formatted(Formatting.RED).formatted(Formatting.BOLD));
+ } else {
+ lines.add(Text.literal(String.format("%-21s", "Floor Tier:") + itemTier + " (" + itemTierFloors.get(itemTier) + ")").formatted(Formatting.BLUE));
+ }
+ }
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java
index 02b694b6..7413e06f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/rift/TheRift.java
@@ -8,11 +8,6 @@ import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
public class TheRift {
- /**
- * @see de.hysky.skyblocker.utils.Utils#isInTheRift() Utils#isInTheRift().
- */
- public static final String LOCATION = "rift";
-
public static void init() {
WorldRenderEvents.AFTER_TRANSLUCENT.register(MirrorverseWaypoints::render);
WorldRenderEvents.AFTER_TRANSLUCENT.register(EffigyWaypoints::render);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java
index e1545c6c..b8907e27 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/OverlayScreen.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.searchOverlay;
+package de.hysky.skyblocker.skyblock.searchoverlay;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import net.minecraft.client.gui.DrawContext;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java
index b2a453a9..1d740601 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/searchoverlay/SearchOverManager.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.searchOverlay;
+package de.hysky.skyblocker.skyblock.searchoverlay;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
@@ -96,13 +96,15 @@ public class SearchOverManager {
HashMap<String, String> namesToId = new HashMap<>();
//get bazaar items
- try (Http.ApiResponse response = Http.sendHypixelRequest("skyblock/bazaar", "")) {
- JsonObject products = JsonParser.parseString(response.content()).getAsJsonObject().get("products").getAsJsonObject();
+ try {
+ if (TooltipInfoType.BAZAAR.getData() == null) TooltipInfoType.BAZAAR.run();
+
+ JsonObject products = TooltipInfoType.BAZAAR.getData();
for (Map.Entry<String, JsonElement> entry : products.entrySet()) {
if (entry.getValue().isJsonObject()) {
JsonObject product = entry.getValue().getAsJsonObject();
- String id = product.get("product_id").getAsString();
- int sellVolume = product.get("quick_status").getAsJsonObject().get("sellVolume").getAsInt();
+ String id = product.get("id").getAsString();
+ int sellVolume = product.get("sellVolume").getAsInt();
if (sellVolume == 0)
continue; //do not add items that do not sell e.g. they are not actual in the bazaar
Matcher matcher = BAZAAR_ENCHANTMENT_PATTERN.matcher(id);
@@ -334,7 +336,7 @@ public class SearchOverManager {
}
}
- static Pair<String, String> splitString(String s) {
+ public static Pair<String, String> splitString(String s) {
if (s.length() <= 15) {
return Pair.of(s, "");
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java
index 82394a78..96ab35d5 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/util/Ico.java
@@ -60,4 +60,5 @@ public class Ico {
public static final ItemStack EXPERIENCE_BOTTLE = new ItemStack(Items.EXPERIENCE_BOTTLE);
public static final ItemStack PINK_DYE = new ItemStack(Items.PINK_DYE);
public static final ItemStack ENCHANTED_BOOK = new ItemStack(Items.ENCHANTED_BOOK);
+ public static final ItemStack SPIDER_EYE = new ItemStack(Items.SPIDER_EYE);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
index 6f40f5a8..309ba9ca 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/DungeonSecretWidget.java
@@ -1,16 +1,19 @@
package de.hysky.skyblocker.skyblock.tabhud.widget;
import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
+import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
+import java.util.regex.Pattern;
+
// this widget shows info about the secrets of the dungeon
public class DungeonSecretWidget extends Widget {
- private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE,
- Formatting.BOLD);
+ private static final MutableText TITLE = Text.literal("Discoveries").formatted(Formatting.DARK_PURPLE, Formatting.BOLD);
+ private static final Pattern DISCOVERIES = Pattern.compile("Discoveries: (\\d+)");
public DungeonSecretWidget() {
super(TITLE, Formatting.DARK_PURPLE.getColorValue());
@@ -18,9 +21,12 @@ public class DungeonSecretWidget extends Widget {
@Override
public void updateContent() {
- this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31);
- this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32);
-
+ if (PlayerListMgr.regexAt(31, DISCOVERIES) != null) {
+ this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 32);
+ this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 33);
+ } else {
+ this.addSimpleIcoText(Ico.CHEST, "Secrets:", Formatting.YELLOW, 31);
+ this.addSimpleIcoText(Ico.SKULL, "Crypts:", Formatting.YELLOW, 32);
+ }
}
-
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java
new file mode 100644
index 00000000..6751ea5d
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/SpidersDenServerWidget.java
@@ -0,0 +1,87 @@
+package de.hysky.skyblocker.skyblock.tabhud.widget;
+
+import de.hysky.skyblocker.skyblock.tabhud.util.Ico;
+import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListMgr;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+
+import java.util.Arrays;
+
+/**
+ * This widget shows info about the Spider's Den server
+ */
+public class SpidersDenServerWidget extends Widget {
+
+ private static final MutableText TITLE =
+ Text.literal("Server Info").formatted(Formatting.DARK_AQUA, Formatting.BOLD);
+
+ /**
+ * Broodmother Mini-Boss tab states
+ */
+ private enum BroodmotherState {
+ SOON("Soon", Formatting.GOLD),
+ AWAKENING("Awakening", Formatting.GOLD),
+ IMMINENT("Imminent", Formatting.DARK_RED),
+ ALIVE("Alive!", Formatting.DARK_RED),
+ SLAIN("Slain", Formatting.YELLOW),
+ DORMANT("Dormant", Formatting.YELLOW),
+ UNKNOWN("Unknown", Formatting.GRAY);
+
+ private final String text;
+ private final Formatting formatting;
+
+ BroodmotherState(String text, Formatting formatting) {
+ this.text = text;
+ this.formatting = formatting;
+ }
+
+ public String text() {
+ return this.text;
+ }
+
+ public Formatting formatting() {
+ return this.formatting;
+ }
+
+ /**
+ * Returns a state object by text
+ *
+ * @param text text state from tab
+ * @return Broodmother State object
+ */
+ public static BroodmotherState from(String text) {
+ return Arrays.stream(BroodmotherState.values())
+ .filter(broodmotherState -> text.equals(broodmotherState.text())).findFirst().orElse(UNKNOWN);
+ }
+ }
+
+ public SpidersDenServerWidget() {
+ super(TITLE, Formatting.DARK_AQUA.getColorValue());
+ }
+
+ /**
+ * Parses the Broodmother string from tab and returns a state object.
+ *
+ * @return Broodmother State object
+ */
+ private static BroodmotherState parseTab() {
+ String state = PlayerListMgr.strAt(45);
+ if (state == null || !state.contains(": ")) return BroodmotherState.UNKNOWN;
+
+ return BroodmotherState.from(state.split(": ")[1]);
+ }
+
+ /**
+ * Updates the information in the widget.
+ */
+ @Override
+ public void updateContent() {
+ this.addSimpleIcoText(Ico.MAP, "Area:", Formatting.DARK_AQUA, 41);
+ this.addSimpleIcoText(Ico.NTAG, "Server ID:", Formatting.GRAY, 42);
+ this.addSimpleIcoText(Ico.EMERALD, "Gems:", Formatting.GREEN, 43);
+
+ BroodmotherState broodmotherState = parseTab();
+ this.addSimpleIcoText(Ico.SPIDER_EYE, "Broodmother: ", broodmotherState.formatting(), broodmotherState.text());
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java
index 17079d15..58deced2 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Http.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Http.java
@@ -53,7 +53,7 @@ public class Http {
String body = new String(decodedInputStream.readAllBytes());
HttpHeaders headers = response.headers();
- return new ApiResponse(body, response.statusCode(), getCacheStatus(headers), getAge(headers));
+ return new ApiResponse(body, response.statusCode(), getCacheStatuses(headers), getAge(headers));
}
public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException {
@@ -115,12 +115,12 @@ public class Http {
}
/**
- * Returns the cache status of the resource
+ * Returns the cache statuses of the resource. All possible cache status values conform to Cloudflare's.
*
- * @see <a href="https://developers.cloudflare.com/cache/concepts/default-cache-behavior/#cloudflare-cache-responses">Cloudflare Cache Docs</a>
+ * @see <a href="https://developers.cloudflare.com/cache/concepts/cache-responses/">Cloudflare Cache Docs</a>
*/
- private static String getCacheStatus(HttpHeaders headers) {
- return headers.firstValue("CF-Cache-Status").orElse("UNKNOWN");
+ private static String[] getCacheStatuses(HttpHeaders headers) {
+ return new String[] { headers.firstValue("CF-Cache-Status").orElse("UNKNOWN"), headers.firstValue("Local-Cache-Status").orElse("UNKNOWN") };
}
private static int getAge(HttpHeaders headers) {
@@ -128,7 +128,7 @@ public class Http {
}
//TODO If ever needed, we could just replace cache status with the response headers and go from there
- public record ApiResponse(String content, int statusCode, String cacheStatus, int age) implements AutoCloseable {
+ public record ApiResponse(String content, int statusCode, String[] cacheStatuses, int age) implements AutoCloseable {
public boolean ok() {
return statusCode == 200;
@@ -139,7 +139,7 @@ public class Http {
}
public boolean cached() {
- return cacheStatus.equals("HIT");
+ return cacheStatuses[0].equals("HIT") || cacheStatuses[1].equals("HIT");
}
@Override
diff --git a/src/main/java/de/hysky/skyblocker/utils/Location.java b/src/main/java/de/hysky/skyblocker/utils/Location.java
new file mode 100644
index 00000000..bd2773fd
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/Location.java
@@ -0,0 +1,116 @@
+package de.hysky.skyblocker.utils;
+
+import java.util.Arrays;
+
+/**
+ * All Skyblock locations
+ */
+public enum Location {
+ /**
+ * mode: dynamic
+ */
+ PRIVATE_ISLAND("dynamic"),
+ /**
+ * mode: garden
+ */
+ GARDEN("garden"),
+ /**
+ * mode: hub
+ */
+ HUB("hub"),
+ /**
+ * mode: farming_1
+ */
+ THE_FARMING_ISLAND("farming_1"),
+ /**
+ * mode: foraging_1
+ */
+ THE_PARK("foraging_1"),
+ /**
+ * mode: combat_1
+ */
+ SPIDERS_DEN("combat_1"),
+ /**
+ * mode: combat_2
+ */
+ BLAZING_FORTRESS("combat_2"),
+ /**
+ * mode: combat_3
+ */
+ THE_END("combat_3"),
+ /**
+ * mode: crimson_isle
+ */
+ CRIMSON_ISLE("crimson_isle"),
+ /**
+ * mode: mining_1
+ */
+ GOLD_MINE("mining_1"),
+ /**
+ * mode: mining_2
+ */
+ DEEP_CAVERNS("mining_2"),
+ /**
+ * mode: mining_3
+ */
+ DWARVEN_MINES("mining_3"),
+ /**
+ * mode: dungeon_hub
+ */
+ DUNGEON_HUB("dungeon_hub"),
+ /**
+ * mode: winter
+ */
+ WINTER_ISLAND("winter"),
+ /**
+ * mode: rift
+ */
+ THE_RIFT("rift"),
+ /**
+ * mode: dark_auction
+ */
+ DARK_AUCTION("dark_auction"),
+ /**
+ * mode: crystal_hollows
+ */
+ CRYSTAL_HOLLOWS("crystal_hollows"),
+ /**
+ * mode: dungeon
+ */
+ DUNGEON("dungeon"),
+ /**
+ * mode: kuudra
+ */
+ KUUDRAS_HOLLOW("kuudra"),
+ /**
+ * Unknown Skyblock location
+ */
+ UNKNOWN("unknown");
+
+ /**
+ * location id from <a href="https://api.hypixel.net/v2/resources/games">Hypixel API</a>
+ */
+ private final String id;
+
+ /**
+ * @param id location id from <a href="https://api.hypixel.net/v2/resources/games">Hypixel API</a>
+ */
+ Location(String id) {
+ this.id = id;
+ }
+
+ /**
+ * @return location id
+ */
+ public String id() {
+ return this.id;
+ }
+
+ /**
+ * @param id location id from <a href="https://api.hypixel.net/v2/resources/games">Hypixel API</a>
+ * @return location object
+ */
+ public static Location from(String id) {
+ return Arrays.stream(Location.values()).filter(loc -> id.equals(loc.id())).findFirst().orElse(UNKNOWN);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java
index cd739a0c..08d0b167 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Utils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java
@@ -3,10 +3,8 @@ package de.hysky.skyblocker.utils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import de.hysky.skyblocker.events.SkyblockEvents;
-import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra;
import de.hysky.skyblocker.skyblock.item.MuseumItemCache;
import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
-import de.hysky.skyblocker.skyblock.rift.TheRift;
import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
@@ -37,15 +35,17 @@ import java.util.concurrent.CompletableFuture;
public class Utils {
private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
private static final String ALTERNATE_HYPIXEL_ADDRESS = System.getProperty("skyblocker.alternateHypixelAddress", "");
- private static final String DUNGEONS_LOCATION = "dungeon";
- private static final String CRYSTAL_HOLLOWS_LOCATION = "crystal_hollows";
- private static final String DWARVEN_MINES_LOCATION = "mining_3";
private static final String PROFILE_PREFIX = "Profile: ";
private static boolean isOnHypixel = false;
private static boolean isOnSkyblock = false;
private static boolean isInjected = false;
/**
+ * Current Skyblock location (from /locraw)
+ */
+ @NotNull
+ private static Location location = Location.UNKNOWN;
+ /**
* The profile name parsed from the player list.
*/
@NotNull
@@ -88,31 +88,30 @@ public class Utils {
}
public static boolean isInDungeons() {
- return getLocationRaw().equals(DUNGEONS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ return location == Location.DUNGEON || FabricLoader.getInstance().isDevelopmentEnvironment();
}
public static boolean isInCrystalHollows() {
- return getLocationRaw().equals(CRYSTAL_HOLLOWS_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ return location == Location.CRYSTAL_HOLLOWS || FabricLoader.getInstance().isDevelopmentEnvironment();
}
public static boolean isInDwarvenMines() {
- return getLocationRaw().equals(DWARVEN_MINES_LOCATION) || FabricLoader.getInstance().isDevelopmentEnvironment();
+ return location == Location.DWARVEN_MINES || FabricLoader.getInstance().isDevelopmentEnvironment();
}
public static boolean isInTheRift() {
- return getLocationRaw().equals(TheRift.LOCATION);
+ return location == Location.THE_RIFT;
}
/**
* @return if the player is in the end island
*/
public static boolean isInTheEnd() {
- // /locraw returns "combat_3" when in The End
- return getLocationRaw().equals("combat_3");
+ return location == Location.THE_END;
}
public static boolean isInKuudra() {
- return getLocationRaw().equals(Kuudra.LOCATION);
+ return location == Location.KUUDRAS_HOLLOW;
}
public static boolean isInjected() {
@@ -133,6 +132,14 @@ public class Utils {
}
/**
+ * @return the location parsed from /locraw.
+ */
+ @NotNull
+ public static Location getLocation() {
+ return location;
+ }
+
+ /**
* @return the server parsed from /locraw.
*/
@NotNull
@@ -376,31 +383,45 @@ public class Utils {
}
/**
+ * Parses /locraw chat message and updates {@link #server}, {@link #gameType}, {@link #locationRaw}, {@link #map}
+ * and {@link #location}
+ *
+ * @param message json message from chat
+ */
+ private static void parseLocRaw(String message) {
+ JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject();
+
+ if (locRaw.has("server")) {
+ server = locRaw.get("server").getAsString();
+ }
+ if (locRaw.has("gameType")) {
+ gameType = locRaw.get("gameType").getAsString();
+ }
+ if (locRaw.has("mode")) {
+ locationRaw = locRaw.get("mode").getAsString();
+ location = Location.from(locationRaw);
+ } else {
+ location = Location.UNKNOWN;
+ }
+ if (locRaw.has("map")) {
+ map = locRaw.get("map").getAsString();
+ }
+ }
+
+ /**
* Parses the /locraw reply from the server and updates the player's profile id
*
* @return not display the message in chat if the command is sent by the mod
*/
public static boolean onChatMessage(Text text, boolean overlay) {
String message = text.getString();
- if (message.startsWith("{\"server\":") && message.endsWith("}")) {
- JsonObject locRaw = JsonParser.parseString(message).getAsJsonObject();
- if (locRaw.has("server")) {
- server = locRaw.get("server").getAsString();
- if (locRaw.has("gameType")) {
- gameType = locRaw.get("gameType").getAsString();
- }
- if (locRaw.has("mode")) {
- locationRaw = locRaw.get("mode").getAsString();
- }
- if (locRaw.has("map")) {
- map = locRaw.get("map").getAsString();
- }
- boolean shouldFilter = !sentLocRaw;
- sentLocRaw = false;
+ if (message.startsWith("{\"server\":") && message.endsWith("}")) {
+ parseLocRaw(message);
+ boolean shouldFilter = !sentLocRaw;
+ sentLocRaw = false;
- return shouldFilter;
- }
+ return shouldFilter;
}
if (isOnSkyblock && message.startsWith("Profile ID: ")) {
@@ -419,6 +440,7 @@ public class Utils {
gameType = "";
locationRaw = "";
map = "";
+ location = Location.UNKNOWN;
}
private static void tickMayorCache(boolean refresh) {
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index 52cc52c4..25818e6d 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -89,7 +89,7 @@
"text.autoconfig.skyblocker.option.general.itemTooltip.enableExoticTooltip": "Enable Exotic Tooltip",
"text.autoconfig.skyblocker.option.general.itemTooltip.enableExoticTooltip.@Tooltip": "Displays the type of exotic below the item's name if an armor piece is exotic.",
"text.autoconfig.skyblocker.option.general.dungeonQuality": "Dungeon Quality",
- "text.autoconfig.skyblocker.option.general.dungeonQuality.@Tooltip": "Displays quality of dungeon drops from mobs",
+ "text.autoconfig.skyblocker.option.general.dungeonQuality.@Tooltip": "Displays quality and tier of dungeon drops from mobs.\n\n\nReminder:\nTier 1-3 dropped from F1-F3\nTier 4-7 dropped from F4-F7 or M1-M4\nTier 8-10 are dropped only from M5-M7",
"text.autoconfig.skyblocker.option.general.itemInfoDisplay": "Item Info Display",
"text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo": "Attribute Shard Info",
"text.autoconfig.skyblocker.option.general.itemInfoDisplay.attributeShardInfo.@Tooltip": "Displays the attribute's level as the stack count and the initials of the attribute's name.",
@@ -271,6 +271,8 @@
"text.autoconfig.skyblocker.option.locations.dungeons.solveWaterboard.@Tooltip": "Click the levers with green boxes to solve the puzzle.",
"text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder": "Solve Boulder Puzzle",
"text.autoconfig.skyblocker.option.locations.dungeons.solveBoulder.@Tooltip": "Draws a line to the chest and highlight button",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveIceFill": "Solve Ice Fill Puzzle",
+ "text.autoconfig.skyblocker.option.locations.dungeons.solveSilverfish": "Solve Silverfish Puzzle",
"text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage": "Mimic Message",
"text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage": "Enable Mimic Message",
"text.autoconfig.skyblocker.option.locations.dungeons.mimicMessage.sendMimicMessage.@Tooltip": "Sends a message in chat upon killing a mimic for other players' score calculation mods.",
@@ -456,7 +458,7 @@
"skyblocker.dungeons.secrets.customWaypointNotFound": "§cNo custom waypoint found at X: %d, Y: %d, Z: %d for room %s.",
"skyblocker.dungeons.dungeonScore.scoreText": "Score: %s",
"skyblocker.dungeons.puzzle.boulder.noSolution": "No solution found!",
-
+
"skyblocker.dungeons.secretsTracker.feedback": "%s§f found %s§f secrets. %s",
"skyblocker.dungeons.secretsTracker.failFeedback": "§cUnable to calculate the amount of secrets everybody did this run!",
@@ -574,7 +576,7 @@
"skyblocker.partyFinder.yourParty": "Your party",
"skyblocker.partyFinder.deList": "Click to de-list",
"skyblocker.partyFinder.join": "Click to join",
-
+
"skyblocker.crimson.kuudra.noArrowPoison": "No Arrow Poison!",
"skyblocker.crimson.kuudra.lowArrowPoison": "Low on Arrow Poison!",
diff --git a/src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json b/src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json
new file mode 100644
index 00000000..faa437ac
--- /dev/null
+++ b/src/main/resources/assets/skyblocker/tabhud/standard/spider_den.json
@@ -0,0 +1,17 @@
+{
+ "widgets": [
+ {
+ "name": "SpidersDenServerWidget",
+ "alias": "psw"
+ }
+ ],
+ "layout": [
+ {
+ "op": "place",
+ "where": "center",
+ "apply_to": [
+ "psw"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json b/src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json
new file mode 100644
index 00000000..0df51096
--- /dev/null
+++ b/src/main/resources/resourcepacks/top_aligned/assets/skyblocker/tabhud/standard/spider_den.json
@@ -0,0 +1,17 @@
+{
+ "widgets": [
+ {
+ "name": "SpidersDenServerWidget",
+ "alias": "psw"
+ }
+ ],
+ "layout": [
+ {
+ "op": "place",
+ "where": "centerTop",
+ "apply_to": [
+ "psw"
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java
new file mode 100644
index 00000000..a3cbb93f
--- /dev/null
+++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/IceFillTest.java
@@ -0,0 +1,29 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import org.joml.Vector2i;
+import org.joml.Vector2ic;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class IceFillTest {
+ private static final boolean[][] iceFillBoard = new boolean[][]{
+ {false, false, true, false, false, false, false},
+ {false, false, false, false, false, false, false},
+ {false, false, false, true, true, false, false},
+ {false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false},
+ {true, false, false, false, false, false, false},
+ };
+ private static final List<Vector2ic> iceFillPath = new ArrayList<>();
+
+ @Test
+ void testIceFillSolve() {
+ IceFill.INSTANCE.solve(iceFillBoard, iceFillPath);
+ List<Vector2ic> expectedIceFillPath = List.of(new Vector2i(6, 3), new Vector2i(5, 3), new Vector2i(4, 3), new Vector2i(3, 3), new Vector2i(3, 2), new Vector2i(4, 2), new Vector2i(5, 2), new Vector2i(6, 2), new Vector2i(6, 1), new Vector2i(5, 1), new Vector2i(5, 0), new Vector2i(4, 0), new Vector2i(4, 1), new Vector2i(3, 1), new Vector2i(3, 0), new Vector2i(2, 0), new Vector2i(1, 0), new Vector2i(0, 0), new Vector2i(0, 1), new Vector2i(1, 1), new Vector2i(2, 1), new Vector2i(2, 2), new Vector2i(1, 2), new Vector2i(1, 3), new Vector2i(1, 4), new Vector2i(1, 5), new Vector2i(2, 5), new Vector2i(3, 5), new Vector2i(3, 4), new Vector2i(4, 4), new Vector2i(5, 4), new Vector2i(6, 4), new Vector2i(6, 5), new Vector2i(6, 6), new Vector2i(5, 6), new Vector2i(5, 5), new Vector2i(4, 5), new Vector2i(4, 6), new Vector2i(3, 6), new Vector2i(2, 6), new Vector2i(1, 6), new Vector2i(0, 6), new Vector2i(0, 5), new Vector2i(0, 4), new Vector2i(0, 3));
+ Assertions.assertEquals(expectedIceFillPath, iceFillPath);
+ }
+}
diff --git a/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java
new file mode 100644
index 00000000..cc6178e1
--- /dev/null
+++ b/src/test/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/SilverfishTest.java
@@ -0,0 +1,40 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import org.joml.Vector2i;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+public class SilverfishTest {
+ private static final boolean[][] silverfishBoard = new boolean[][]{
+ {false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false},
+ {false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false},
+ {true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false},
+ {false, true, false, false, false, false, false, false, false, false, true, false, false, false, false, true, false},
+ {false, false, true, false, false, false, false, false, false, false, false, true, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false},
+ {false, false, false, false, false, false, true, false, false, false, true, false, false, false, false, false, false},
+ {false, false, true, false, false, false, false, false, false, false, false, false, false, true, false, false, false}
+ };
+
+ @Test
+ void testSilverfishSolve() {
+ for (int i = 0; i < silverfishBoard.length; i++) {
+ System.arraycopy(silverfishBoard[i], 0, Silverfish.INSTANCE.silverfishBoard[i], 0, silverfishBoard[i].length);
+ }
+ Silverfish.INSTANCE.silverfishPos = new Vector2i(15, 15);
+ Silverfish.INSTANCE.solve();
+ List<Vector2i> expectedSilverfishPath = List.of(new Vector2i(15, 15), new Vector2i(15, 11), new Vector2i(16, 11), new Vector2i(16, 3), new Vector2i(0, 3), new Vector2i(0, 4), new Vector2i(1, 4), new Vector2i(1, 2), new Vector2i(10, 2), new Vector2i(10, 9), new Vector2i(0, 9));
+ Assertions.assertEquals(expectedSilverfishPath, Silverfish.INSTANCE.silverfishPath);
+ }
+}
diff --git a/src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java b/src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java
index c397b5a4..a9fd541f 100644
--- a/src/test/java/de/hysky/skyblocker/skyblock/searchOverlay/SplitStringTest.java
+++ b/src/test/java/de/hysky/skyblocker/skyblock/searchoverlay/SplitStringTest.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.searchOverlay;
+package de.hysky.skyblocker.skyblock.searchoverlay;
import it.unimi.dsi.fastutil.Pair;
import org.junit.jupiter.api.Assertions;