diff options
9 files changed, 455 insertions, 13 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index b8722fc6..dca41bd1 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -8,18 +8,16 @@ import de.hysky.skyblocker.skyblock.*; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; 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; @@ -117,6 +115,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 b5ddcf5d..78458291 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/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/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 3c6c6349..60631ef1 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -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.", @@ -408,7 +410,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!", @@ -526,7 +528,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/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); + } +} |