aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java26
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java16
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java7
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java24
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Cell.java51
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Switch.java39
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java450
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java12
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java16
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Tickable.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java8
14 files changed, 604 insertions, 60 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index 9ce0df8d..190bda4f 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -12,6 +12,7 @@ import de.hysky.skyblocker.skyblock.dungeon.LividColor;
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.DwarvenHud;
@@ -104,6 +105,7 @@ public class SkyblockerMod implements ClientModInitializer {
DungeonMap.init();
DungeonManager.init();
DungeonBlaze.init();
+ Waterboard.init();
ChestValue.init();
FireFreezeStaffTimer.init();
GuardianHealth.init();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java
index 8de1e3fe..db195003 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java
@@ -37,13 +37,13 @@ public class CreeperBeams extends DungeonPuzzle {
private static final int FLOOR_Y = 68;
private static final int BASE_Y = 74;
- private static final CreeperBeams INSTANCE = new CreeperBeams("creeper", "creeper-room");
+ private static final CreeperBeams INSTANCE = new CreeperBeams();
private static ArrayList<Beam> beams = new ArrayList<>();
private static BlockPos base = null;
- private CreeperBeams(String puzzleName, String... roomName) {
- super(puzzleName, roomName);
+ private CreeperBeams() {
+ super("creeper", "creeper-room");
}
public static void init() {
@@ -57,38 +57,34 @@ public class CreeperBeams extends DungeonPuzzle {
}
@Override
- public void tick() {
+ public void tick(MinecraftClient client) {
// don't do anything if the room is solved
if (!shouldSolve()) {
return;
}
- MinecraftClient client = MinecraftClient.getInstance();
- ClientWorld world = client.world;
- ClientPlayerEntity player = client.player;
-
// clear state if not in dungeon
- if (world == null || player == null || !Utils.isInDungeons()) {
+ if (client.world == null || client.player == null || !Utils.isInDungeons()) {
return;
}
// try to find base if not found and solve
if (base == null) {
- base = findCreeperBase(player, world);
+ base = findCreeperBase(client.player, client.world);
if (base == null) {
return;
}
Vec3d creeperPos = new Vec3d(base.getX() + 0.5, BASE_Y + 1.75, base.getZ() + 0.5);
- ArrayList<BlockPos> targets = findTargets(world, base);
+ ArrayList<BlockPos> targets = findTargets(client.world, base);
beams = findLines(creeperPos, targets);
}
// update the beam states
- beams.forEach(b -> b.updateState(world));
+ beams.forEach(b -> b.updateState(client.world));
// check if the room is solved
- if (!isTarget(world, base)) {
+ if (!isTarget(client.world, base)) {
reset();
}
}
@@ -239,11 +235,11 @@ public class CreeperBeams extends DungeonPuzzle {
if (toDo) {
RenderHelper.renderOutline(wrc, outlineOne, color, 3, false);
RenderHelper.renderOutline(wrc, outlineTwo, color, 3, false);
- RenderHelper.renderLinesFromPoints(wrc, line, color, 1, 2);
+ RenderHelper.renderLinesFromPoints(wrc, line, color, 1, 2, false);
} else {
RenderHelper.renderOutline(wrc, outlineOne, GREEN_COLOR_COMPONENTS, 1, false);
RenderHelper.renderOutline(wrc, outlineTwo, GREEN_COLOR_COMPONENTS, 1, false);
- RenderHelper.renderLinesFromPoints(wrc, line, GREEN_COLOR_COMPONENTS, 0.75f, 1);
+ RenderHelper.renderLinesFromPoints(wrc, line, GREEN_COLOR_COMPONENTS, 0.75f, 1, false);
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java
index 5774eaef..6b435d3c 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java
@@ -26,15 +26,15 @@ public class DungeonBlaze extends DungeonPuzzle {
private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName());
private static final float[] GREEN_COLOR_COMPONENTS = {0.0F, 1.0F, 0.0F};
private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f};
- private static final DungeonBlaze INSTANCE = new DungeonBlaze("blaze", "blaze-room-1-high", "blaze-room-1-low");
+ private static final DungeonBlaze INSTANCE = new DungeonBlaze();
private static ArmorStandEntity highestBlaze = null;
private static ArmorStandEntity lowestBlaze = null;
private static ArmorStandEntity nextHighestBlaze = null;
private static ArmorStandEntity nextLowestBlaze = null;
- private DungeonBlaze(String puzzleName, String... roomName) {
- super(puzzleName, roomName);
+ private DungeonBlaze() {
+ super("blaze", "blaze-room-1-high", "blaze-room-1-low");
}
public static void init() {
@@ -44,14 +44,12 @@ public class DungeonBlaze extends DungeonPuzzle {
* Updates the state of Blaze entities and triggers the rendering process if necessary.
*/
@Override
- public void tick() {
+ public void tick(MinecraftClient client) {
if (!shouldSolve()) {
return;
}
- ClientWorld world = MinecraftClient.getInstance().world;
- ClientPlayerEntity player = MinecraftClient.getInstance().player;
- if (world == null || player == null || !Utils.isInDungeons()) return;
- List<ObjectIntPair<ArmorStandEntity>> blazes = getBlazesInWorld(world, player);
+ if (client.world == null || client.player == null || !Utils.isInDungeons()) return;
+ List<ObjectIntPair<ArmorStandEntity>> blazes = getBlazesInWorld(client.world, client.player);
sortBlazes(blazes);
updateBlazeEntities(blazes);
}
@@ -143,7 +141,7 @@ public class DungeonBlaze extends DungeonPuzzle {
Vec3d blazeCenter = blazeBox.getCenter();
Vec3d nextBlazeCenter = nextBlazeBox.getCenter();
- RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f);
+ RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f, false);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java
index 04446e60..f5e0461d 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java
@@ -17,7 +17,7 @@ import java.util.Set;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
public abstract class DungeonPuzzle implements Tickable, Renderable {
- private final String puzzleName;
+ protected final String puzzleName;
@NotNull
private final Set<String> roomNames;
private boolean shouldSolve;
@@ -35,16 +35,17 @@ public abstract class DungeonPuzzle implements Tickable, Renderable {
shouldSolve = true;
}
});
- ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("solvePuzzle").then(literal(puzzleName).executes(context -> {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName).then(literal("solve").executes(context -> {
Room currentRoom = DungeonManager.getCurrentRoom();
if (currentRoom != null) {
+ reset();
currentRoom.addSubProcess(this);
context.getSource().sendFeedback(Constants.PREFIX.get().append("§aSolving " + puzzleName + " puzzle in the current room."));
} else {
context.getSource().sendError(Constants.PREFIX.get().append("§cCurrent room is null."));
}
return Command.SINGLE_SUCCESS;
- }))))));
+ })))))));
ClientPlayConnectionEvents.JOIN.register((handler, sender, client) -> reset());
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java
index 90028a4f..c8043288 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java
@@ -8,8 +8,6 @@ import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
-import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.decoration.ItemFrameEntity;
import net.minecraft.item.FilledMapItem;
import net.minecraft.item.map.MapState;
@@ -27,33 +25,29 @@ import java.util.List;
public class TicTacToe extends DungeonPuzzle {
private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class);
private static final float[] RED_COLOR_COMPONENTS = {1.0F, 0.0F, 0.0F};
- private static final TicTacToe INSTANCE = new TicTacToe("tic-tac-toe", "tic-tac-toe-1");
+ private static final TicTacToe INSTANCE = new TicTacToe();
private static Box nextBestMoveToMake = null;
- private TicTacToe(String puzzleName, String... roomName) {
- super(puzzleName, roomName);
+ private TicTacToe() {
+ super("tic-tac-toe", "tic-tac-toe-1");
}
public static void init() {
}
@Override
- public void tick() {
+ public void tick(MinecraftClient client) {
if (!shouldSolve()) {
return;
}
- MinecraftClient client = MinecraftClient.getInstance();
- ClientWorld world = client.world;
- ClientPlayerEntity player = client.player;
-
nextBestMoveToMake = null;
- if (world == null || player == null || !Utils.isInDungeons()) return;
+ if (client.world == null || client.player == null || !Utils.isInDungeons()) return;
//Search within 21 blocks for item frames that contain maps
- Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21);
- List<ItemFrameEntity> itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap);
+ Box searchBox = new Box(client.player.getX() - 21, client.player.getY() - 21, client.player.getZ() - 21, client.player.getX() + 21, client.player.getY() + 21, client.player.getZ() + 21);
+ List<ItemFrameEntity> itemFramesThatHoldMaps = client.world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap);
try {
//Only attempt to solve if its the player's turn
@@ -64,7 +58,7 @@ public class TicTacToe extends DungeonPuzzle {
char facing = 'X';
for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) {
- MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt()));
+ MapState mapState = client.world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt()));
if (mapState == null) continue;
@@ -86,7 +80,7 @@ public class TicTacToe extends DungeonPuzzle {
facing = 'Z';
}
- Block block = world.getBlockState(blockPos).getBlock();
+ Block block = client.world.getBlockState(blockPos).getBlock();
if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) {
leftmostRow = blockPos;
column = i;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Cell.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Cell.java
new file mode 100644
index 00000000..0279fed8
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Cell.java
@@ -0,0 +1,51 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard;
+
+public class Cell {
+ public static final Cell BLOCK = new Cell(Type.BLOCK);
+ public static final Cell EMPTY = new Cell(Type.EMPTY);
+ public final Type type;
+
+ private Cell(Type type) {
+ this.type = type;
+ }
+
+ public boolean isOpen() {
+ return type == Type.EMPTY;
+ }
+
+ public static class SwitchCell extends Cell {
+ public final int id;
+ private boolean open;
+
+ public SwitchCell(int id) {
+ super(Type.SWITCH);
+ this.id = id;
+ }
+
+ public static SwitchCell ofOpened(int id) {
+ SwitchCell switchCell = new SwitchCell(id);
+ switchCell.open = true;
+ return switchCell;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return super.equals(obj) || obj instanceof SwitchCell switchCell && id == switchCell.id && open == switchCell.open;
+ }
+
+ @Override
+ public boolean isOpen() {
+ return open;
+ }
+
+ public void toggle() {
+ open = !open;
+ }
+ }
+
+ public enum Type {
+ BLOCK,
+ EMPTY,
+ SWITCH
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Switch.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Switch.java
new file mode 100644
index 00000000..bb8da61d
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Switch.java
@@ -0,0 +1,39 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.AbstractCollection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class Switch extends AbstractCollection<Cell.SwitchCell> {
+ public final int id;
+ public final List<Cell.SwitchCell> cells = new ArrayList<>();
+
+ public Switch(int id) {
+ this.id = id;
+ }
+
+ @Override
+ @NotNull
+ public Iterator<Cell.SwitchCell> iterator() {
+ return cells.iterator();
+ }
+
+ @Override
+ public int size() {
+ return cells.size();
+ }
+
+ @Override
+ public boolean add(Cell.SwitchCell cell) {
+ return cells.add(cell);
+ }
+
+ public void toggle() {
+ for (Cell.SwitchCell cell : cells) {
+ cell.toggle();
+ }
+ }
+}
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
new file mode 100644
index 00000000..0006e6e2
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/waterboard/Waterboard.java
@@ -0,0 +1,450 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard;
+
+import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.debug.Debug;
+import de.hysky.skyblocker.skyblock.dungeon.puzzle.DungeonPuzzle;
+import de.hysky.skyblocker.skyblock.dungeon.puzzle.waterboard.Cell.SwitchCell;
+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 de.hysky.skyblocker.utils.scheduler.Scheduler;
+import de.hysky.skyblocker.utils.waypoint.Waypoint;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMaps;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.LeverBlock;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.fluid.Fluids;
+import net.minecraft.fluid.WaterFluid;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.DyeColor;
+import net.minecraft.util.Hand;
+import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.hit.HitResult;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class Waterboard extends DungeonPuzzle {
+ private static final Logger LOGGER = LoggerFactory.getLogger(Waterboard.class);
+ public static final Waterboard INSTANCE = new Waterboard();
+ private static final Object2IntMap<Block> SWITCH_BLOCKS = Object2IntMaps.unmodifiable(new Object2IntOpenHashMap<>(Map.of(
+ Blocks.COAL_BLOCK, 1,
+ Blocks.GOLD_BLOCK, 2,
+ Blocks.QUARTZ_BLOCK, 3,
+ Blocks.DIAMOND_BLOCK, 4,
+ Blocks.EMERALD_BLOCK, 5,
+ Blocks.TERRACOTTA, 6
+ )));
+ private static final BlockPos[] SWITCH_POSITIONS = new BlockPos[]{
+ new BlockPos(20, 61, 10),
+ new BlockPos(20, 61, 15),
+ new BlockPos(20, 61, 20),
+ new BlockPos(10, 61, 20),
+ new BlockPos(10, 61, 15),
+ new BlockPos(10, 61, 10)
+ };
+ public static final BlockPos WATER_LEVER = new BlockPos(15, 60, 5);
+ private static final float[] LIME_COLOR_COMPONENTS = DyeColor.LIME.getColorComponents();
+
+ private CompletableFuture<Void> solve;
+ private final Cell[][] cells = new Cell[19][19];
+ private final Switch[] switches = new Switch[]{new Switch(0), new Switch(1), new Switch(2), new Switch(3), new Switch(4), new Switch(5)};
+ private int doors = 0;
+ private final Result[] results = new Result[64];
+ private int currentCombination;
+ private final IntList bestCombinations = new IntArrayList();
+ private final Waypoint[] waypoints = new Waypoint[7];
+ /**
+ * Used to check the water lever state since the block state does not update immediately after the lever is toggled.
+ */
+ private boolean bestCombinationsUpdated;
+
+ private Waterboard() {
+ super("waterboard", "water-puzzle");
+ UseBlockCallback.EVENT.register(this::onUseBlock);
+ if (Debug.debugEnabled()) {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("puzzle").then(literal(puzzleName)
+ .then(literal("printBoard").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(boardToString(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()));
+ return Command.SINGLE_SUCCESS;
+ }))).then(literal("printCurrentCombination").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(Integer.toBinaryString(INSTANCE.currentCombination)));
+ return Command.SINGLE_SUCCESS;
+ })).then(literal("printBestCombination").executes(context -> {
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(INSTANCE.bestCombinations.toString()));
+ return Command.SINGLE_SUCCESS;
+ }))
+ )))));
+ }
+ }
+
+ public static void init() {
+ }
+
+ private static String boardToString(Cell[][] cells) {
+ StringBuilder sb = new StringBuilder();
+ for (Cell[] row : cells) {
+ sb.append("\n");
+ for (Cell cell : row) {
+ if (cell == null) {
+ sb.append('?');
+ } else if (cell instanceof SwitchCell switchCell) {
+ sb.append(switchCell.id);
+ } else switch (cell.type) {
+ case BLOCK -> sb.append('#');
+ case EMPTY -> sb.append('.');
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public void tick(MinecraftClient client) {
+ if (client.world == null || !DungeonManager.isCurrentRoomMatched() || solve != null && !solve.isDone()) {
+ return;
+ }
+ Room room = DungeonManager.getCurrentRoom();
+ solve = CompletableFuture.runAsync(() -> {
+ Changed changed = updateBoard(client.world, room);
+ if (changed == Changed.NONE) {
+ return;
+ }
+ if (results[0] == null) {
+ updateSwitches();
+ simulateCombinations();
+ clearSwitches();
+ }
+ if (bestCombinations.isEmpty() || changed.doorChanged()) {
+ findBestCombinations();
+ bestCombinationsUpdated = true;
+ }
+ }).exceptionally(e -> {
+ LOGGER.error("[Skyblocker Waterboard] Encountered an unknown exception while solving waterboard.", e);
+ return null;
+ });
+ if (waypoints[0] == null) {
+ for (int i = 0; i < 6; i++) {
+ waypoints[i] = new Waypoint(room.relativeToActual(SWITCH_POSITIONS[i]), Waypoint.Type.HIGHLIGHT, LIME_COLOR_COMPONENTS);
+ }
+ waypoints[6] = new Waypoint(room.relativeToActual(WATER_LEVER), Waypoint.Type.HIGHLIGHT, LIME_COLOR_COMPONENTS);
+ waypoints[6].setFound();
+ }
+ }
+
+ private Changed updateBoard(World world, Room room) {
+ // Parse the waterboard.
+ BlockPos.Mutable pos = new BlockPos.Mutable(24, 78, 26);
+ Changed changed = Changed.NONE;
+ for (int row = 0; row < cells.length; pos.move(cells[row].length, -1, 0), row++) {
+ for (int col = 0; col < cells[row].length; pos.move(Direction.WEST), col++) {
+ Cell cell = parseBlock(world, room, pos);
+ if (!cell.equals(cells[row][col])) {
+ cells[row][col] = cell;
+ changed = changed.onCellChanged();
+ }
+ }
+ }
+
+ // Parse door states.
+ pos.set(15, 57, 15);
+ int prevDoors = doors;
+ doors = 0;
+ for (int i = 0; i < 5; pos.move(Direction.SOUTH), i++) {
+ doors |= world.getBlockState(room.relativeToActual(pos)).isAir() ? 1 << i : 0;
+ }
+ if (doors != prevDoors) {
+ changed = changed.onDoorChanged();
+ }
+
+ // Parse current combination of switches based on the levers.
+ currentCombination = 0;
+ for (int i = 0; i < 6; i++) {
+ currentCombination |= getSwitchState(world, room, i);
+ }
+
+ return changed;
+ }
+
+ private Cell parseBlock(World world, Room room, BlockPos.Mutable pos) {
+ // Check if the block is a switch.
+ BlockState state = world.getBlockState(room.relativeToActual(pos));
+ int switch_ = SWITCH_BLOCKS.getInt(state.getBlock());
+ if (switch_-- > 0) {
+ return new SwitchCell(switch_);
+ }
+ // Check if the block is an opened switch by checking the block behind it.
+ int switchBehind = SWITCH_BLOCKS.getInt(world.getBlockState(room.relativeToActual(pos.move(Direction.SOUTH))).getBlock());
+ pos.move(Direction.NORTH);
+ if (switchBehind-- > 0) {
+ return SwitchCell.ofOpened(switchBehind);
+ }
+
+ // Check if the block is empty otherwise the block is a wall.
+ return state.isAir() || state.isOf(Blocks.WATER) ? Cell.EMPTY : Cell.BLOCK;
+ }
+
+ private static int getSwitchState(World world, Room room, int i) {
+ BlockState state = world.getBlockState(room.relativeToActual(SWITCH_POSITIONS[i]));
+ return state.contains(LeverBlock.POWERED) && state.get(LeverBlock.POWERED) ? 1 << i : 0;
+ }
+
+ private void updateSwitches() {
+ clearSwitches();
+ for (Cell[] row : cells) {
+ for (Cell cell : row) {
+ if (cell instanceof SwitchCell switchCell) {
+ switches[switchCell.id].add(switchCell);
+ }
+ }
+ }
+ }
+
+ private void simulateCombinations() {
+ for (int combination = 0; combination < (1 << 6); combination++) {
+ for (int switchIndex = 0; switchIndex < 6; switchIndex++) {
+ if ((combination & (1 << switchIndex)) != 0) {
+ switches[switchIndex].toggle();
+ }
+ }
+ results[combination] = simulateCombination();
+ for (int switchIndex = 0; switchIndex < 6; switchIndex++) {
+ if ((combination & (1 << switchIndex)) != 0) {
+ switches[switchIndex].toggle();
+ }
+ }
+ }
+ }
+
+ private Result simulateCombination() {
+ List<Vector2i> waters = new ArrayList<>();
+ waters.add(new Vector2i(9, 0));
+ Result result = new Result();
+ while (!waters.isEmpty()) {
+ List<Vector2i> newWaters = new ArrayList<>();
+ for (Iterator<Vector2i> watersIt = waters.iterator(); watersIt.hasNext(); ) {
+ Vector2i water = watersIt.next();
+ // Check if the water has reached a door.
+ if (water.y == 18) {
+ switch (water.x) {
+ case 0 -> result.reachedDoors |= 1 << 4;
+ case 4 -> result.reachedDoors |= 1 << 3;
+ case 9 -> result.reachedDoors |= 1 << 2;
+ case 14 -> result.reachedDoors |= 1 << 1;
+ case 18 -> result.reachedDoors |= 1;
+ }
+ watersIt.remove();
+ continue;
+ }
+ // Check if the water can flow down.
+ if (water.y < 18 && cells[water.y + 1][water.x].isOpen()) {
+ result.putPath(water, 0);
+ water.add(0, 1);
+ continue;
+ }
+
+ // Get the offset to the first block on the left and the right that can flow down.
+ int leftFlowDownOffset = findFlowDown(water, false);
+ int rightFlowDownOffset = findFlowDown(water, true);
+ // Check if left down is in range and is closer than right down.
+ // Note 1: The yarn name "getFlowSpeed" is incorrect as it actually returns the maximum distance that water will check for a hole to flow towards.
+ // Note 2: Skyblock's maximum offset is 5 instead of 4 for some reason.
+ if (-leftFlowDownOffset <= ((WaterFluid) Fluids.WATER).getFlowSpeed(null) + 1 && -leftFlowDownOffset < rightFlowDownOffset) {
+ result.putPath(water, leftFlowDownOffset);
+ water.add(leftFlowDownOffset, 1);
+ continue;
+ }
+ // Check if right down is in range and closer than left down.
+ if (rightFlowDownOffset <= ((WaterFluid) Fluids.WATER).getFlowSpeed(null) + 1 && rightFlowDownOffset < -leftFlowDownOffset) {
+ result.putPath(water, rightFlowDownOffset);
+ water.add(rightFlowDownOffset, 1);
+ continue;
+ }
+
+ // Else flow to both sides if in range.
+ if (leftFlowDownOffset > Integer.MIN_VALUE + 1) {
+ result.putPath(water, leftFlowDownOffset);
+ newWaters.add(new Vector2i(water).add(leftFlowDownOffset, 1));
+ }
+ if (rightFlowDownOffset < Integer.MAX_VALUE) {
+ result.putPath(water, rightFlowDownOffset);
+ newWaters.add(new Vector2i(water).add(rightFlowDownOffset, 1));
+ }
+ watersIt.remove();
+ }
+ waters.addAll(newWaters);
+ }
+ return result;
+ }
+
+ /**
+ * Finds the first block on the left that can flow down.
+ */
+ private int findFlowDown(Vector2i water, boolean direction) {
+ for (int i = 0; water.x + i >= 0 && water.x + i < 19 && i > -8 && i < 8 && cells[water.y][water.x + i].isOpen(); i += direction ? 1 : -1) {
+ if (cells[water.y + 1][water.x + i].isOpen()) {
+ return i;
+ }
+ }
+ return direction ? Integer.MAX_VALUE : Integer.MIN_VALUE + 1;
+ }
+
+ private void findBestCombinations() {
+ bestCombinations.clear();
+ for (int combination = 0, bestScore = 0; combination < (1 << 6); combination++) {
+ int newScore = Integer.bitCount(results[combination].reachedDoors ^ doors);
+ if (newScore >= bestScore) {
+ if (newScore > bestScore) {
+ bestCombinations.clear();
+ bestScore = newScore;
+ }
+ bestCombinations.add(combination);
+ }
+ }
+ }
+
+ @Override
+ public void render(WorldRenderContext context) {
+ if (!DungeonManager.isCurrentRoomMatched()) return;
+ Room room = DungeonManager.getCurrentRoom();
+
+ // Render the best combination.
+ @SuppressWarnings("resource")
+ BlockState state = context.world().getBlockState(room.relativeToActual(WATER_LEVER));
+ // bestCombinationsUpdated is needed because bestCombinations does not update immediately after the lever is turned off.
+ if (waypoints[0] != null && bestCombinationsUpdated && state.contains(LeverBlock.POWERED) && !state.get(LeverBlock.POWERED)) {
+ bestCombinations.intStream().mapToObj(bestCombination -> currentCombination ^ bestCombination).min(Comparator.comparingInt(Integer::bitCount)).ifPresent(bestDifference -> {
+ for (int i = 0; i < 6; i++) {
+ if ((bestDifference & 1 << i) != 0) {
+ waypoints[i].render(context);
+ }
+ }
+ if (bestDifference == 0 && !waypoints[6].shouldRender()) {
+ waypoints[6].setMissing();
+ }
+ });
+ }
+ if (waypoints[6] != null && waypoints[6].shouldRender()) {
+ waypoints[6].render(context);
+ }
+
+ // Render the current path of the water.
+ BlockPos.Mutable pos = new BlockPos.Mutable(15, 79, 26);
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{Vec3d.ofCenter(room.relativeToActual(pos)), Vec3d.ofCenter(room.relativeToActual(pos.move(Direction.DOWN)))}, LIME_COLOR_COMPONENTS, 1f, 5f, true);
+ Result currentResult = results[currentCombination];
+ if (currentResult != null) {
+ for (Map.Entry<Vector2ic, Integer> entry : currentResult.path.entries()) {
+ Vec3d start = Vec3d.ofCenter(room.relativeToActual(pos.set(24 - entry.getKey().x(), 78 - entry.getKey().y(), 26)));
+ Vec3d middle = Vec3d.ofCenter(room.relativeToActual(pos.move(Direction.WEST, entry.getValue())));
+ Vec3d end = Vec3d.ofCenter(room.relativeToActual(pos.move(Direction.DOWN)));
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{start, middle}, LIME_COLOR_COMPONENTS, 1f, 5f, true);
+ RenderHelper.renderLinesFromPoints(context, new Vec3d[]{middle, end}, LIME_COLOR_COMPONENTS, 1f, 5f, true);
+ }
+ }
+ }
+
+ private ActionResult onUseBlock(PlayerEntity player, World world, Hand hand, BlockHitResult blockHitResult) {
+ BlockState state = world.getBlockState(blockHitResult.getBlockPos());
+ if (blockHitResult.getType() == HitResult.Type.BLOCK && waypoints[6] != null && DungeonManager.isCurrentRoomMatched() && blockHitResult.getBlockPos().equals(DungeonManager.getCurrentRoom().relativeToActual(WATER_LEVER)) && state.contains(LeverBlock.POWERED)) {
+ if (!state.get(LeverBlock.POWERED)) {
+ bestCombinationsUpdated = false;
+ Scheduler.INSTANCE.schedule(() -> waypoints[6].setMissing(), 50);
+ }
+ waypoints[6].setFound();
+ }
+ return ActionResult.PASS;
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ solve = null;
+ for (Cell[] row : cells) {
+ Arrays.fill(row, null);
+ }
+ clearSwitches();
+ doors = 0;
+ Arrays.fill(results, null);
+ currentCombination = 0;
+ bestCombinations.clear();
+ Arrays.fill(waypoints, null);
+ }
+
+ public void clearSwitches() {
+ for (Switch switch_ : switches) {
+ switch_.clear();
+ }
+ }
+
+ private enum Changed {
+ NONE, CELL, DOOR, BOTH;
+
+ private boolean cellChanged() {
+ return this == CELL || this == BOTH;
+ }
+
+ private boolean doorChanged() {
+ return this == DOOR || this == BOTH;
+ }
+
+ private Changed onCellChanged() {
+ return switch (this) {
+ case NONE, CELL -> Changed.CELL;
+ case DOOR, BOTH -> Changed.BOTH;
+ };
+ }
+
+ private Changed onDoorChanged() {
+ return switch (this) {
+ case NONE, DOOR -> Changed.DOOR;
+ case CELL, BOTH -> Changed.BOTH;
+ };
+ }
+ }
+
+ public static class Result {
+ private int reachedDoors;
+ private final Multimap<Vector2ic, Integer> path = MultimapBuilder.hashKeys().arrayListValues().build();
+
+ public boolean putPath(Vector2i water, int offset) {
+ return path.put(new Vector2i(water), offset);
+ }
+
+ @Override
+ public String toString() {
+ return "Result[reachedDoors=" + Integer.toBinaryString(reachedDoors) + ", path=" + path + ']';
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
index 70a0fd8c..bd10767f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
@@ -16,9 +16,11 @@ import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.Tickable;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.objects.Object2ByteMap;
+import it.unimi.dsi.fastutil.objects.Object2ByteMaps;
import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
@@ -91,7 +93,7 @@ public class DungeonManager {
* @implNote Not using {@link Registry#getId(Object) Registry#getId(Block)} and {@link Blocks Blocks} since this is also used by {@link de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft.
*/
@SuppressWarnings("JavadocReference")
- protected static final Object2ByteMap<String> NUMERIC_ID = new Object2ByteOpenHashMap<>(Map.ofEntries(
+ protected static final Object2ByteMap<String> NUMERIC_ID = Object2ByteMaps.unmodifiable(new Object2ByteOpenHashMap<>(Map.ofEntries(
Map.entry("minecraft:stone", (byte) 1),
Map.entry("minecraft:diorite", (byte) 2),
Map.entry("minecraft:polished_diorite", (byte) 3),
@@ -113,7 +115,7 @@ public class DungeonManager {
Map.entry("minecraft:gray_terracotta", (byte) 19),
Map.entry("minecraft:cyan_terracotta", (byte) 20),
Map.entry("minecraft:black_terracotta", (byte) 21)
- ));
+ )));
/**
* Block data for dungeon rooms. See {@link de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU} for format details and how it's generated.
* All access to this map must check {@link #isRoomsLoaded()} to prevent concurrent modification.
@@ -502,7 +504,7 @@ public class DungeonManager {
* <li> Create a new room. </li>
* </ul>
* <li> Sets {@link #currentRoom} to the current room, either created from the previous step or from {@link #rooms}. </li>
- * <li> Calls {@link Room#tick()} on {@link #currentRoom}. </li>
+ * <li> Calls {@link Tickable#tick(MinecraftClient)} on {@link #currentRoom}. </li>
* </ul>
*/
@SuppressWarnings("JavadocReference")
@@ -560,7 +562,7 @@ public class DungeonManager {
}
currentRoom = room;
}
- currentRoom.tick();
+ currentRoom.tick(client);
}
/**
@@ -728,7 +730,7 @@ public class DungeonManager {
*
* @return {@code true} if {@link #currentRoom} is not null and {@link #isRoomMatched(Room)}
*/
- private static boolean isCurrentRoomMatched() {
+ public static boolean isCurrentRoomMatched() {
return isRoomMatched(currentRoom);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java
index 4857e8fe..3e25a8f1 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java
@@ -299,20 +299,18 @@ public class Room implements Tickable, Renderable {
*/
@SuppressWarnings("JavadocReference")
@Override
- public void tick() {
- MinecraftClient client = MinecraftClient.getInstance();
- ClientWorld world = client.world;
- if (world == null) {
+ public void tick(MinecraftClient client) {
+ if (client.world == null) {
return;
}
for (Tickable tickable : tickables) {
- tickable.tick();
+ tickable.tick(client);
}
// Wither and blood door
if (SkyblockerConfigManager.get().locations.dungeons.doorHighlight.enableDoorHighlight && doorPos == null) {
- doorPos = DungeonMapUtils.getWitherBloodDoorPos(world, segments);
+ doorPos = DungeonMapUtils.getWitherBloodDoorPos(client.world, segments);
if (doorPos != null) {
doorBox = new Box(doorPos.getX(), doorPos.getY(), doorPos.getZ(), doorPos.getX() + DOOR_SIZE.getX(), doorPos.getY() + DOOR_SIZE.getY(), doorPos.getZ() + DOOR_SIZE.getZ());
}
@@ -329,7 +327,7 @@ public class Room implements Tickable, Renderable {
}
findRoom = CompletableFuture.runAsync(() -> {
for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) {
- if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(world, pos)) {
+ if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(client.world, pos)) {
break;
}
}
@@ -506,14 +504,14 @@ public class Room implements Tickable, Renderable {
/**
* Fails if !{@link #isMatched()}
*/
- protected BlockPos actualToRelative(BlockPos pos) {
+ public BlockPos actualToRelative(BlockPos pos) {
return DungeonMapUtils.actualToRelative(direction, physicalCornerPos, pos);
}
/**
* Fails if !{@link #isMatched()}
*/
- protected BlockPos relativeToActual(BlockPos pos) {
+ public BlockPos relativeToActual(BlockPos pos) {
return DungeonMapUtils.relativeToActual(direction, physicalCornerPos, pos);
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java
index 7849aff7..6629c377 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/waypoint/MythologicalRitual.java
@@ -146,10 +146,10 @@ public class MythologicalRitual {
}
if (burrow.confirmed != TriState.FALSE) {
if (burrow.nextBurrowLine != null) {
- RenderHelper.renderLinesFromPoints(context, burrow.nextBurrowLine, ORANGE_COLOR_COMPONENTS, 0.5F, 5F);
+ RenderHelper.renderLinesFromPoints(context, burrow.nextBurrowLine, ORANGE_COLOR_COMPONENTS, 0.5F, 5F, false);
}
if (burrow.echoBurrowLine != null) {
- RenderHelper.renderLinesFromPoints(context, burrow.echoBurrowLine, ORANGE_COLOR_COMPONENTS, 0.5F, 5F);
+ RenderHelper.renderLinesFromPoints(context, burrow.echoBurrowLine, ORANGE_COLOR_COMPONENTS, 0.5F, 5F, false);
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Tickable.java b/src/main/java/de/hysky/skyblocker/utils/Tickable.java
index 9b7b2e3f..dff34e19 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Tickable.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Tickable.java
@@ -1,5 +1,7 @@
package de.hysky.skyblocker.utils;
+import net.minecraft.client.MinecraftClient;
+
public interface Tickable {
- void tick();
+ void tick(MinecraftClient client);
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
index 7526c0a8..05514d02 100644
--- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
+++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
@@ -140,8 +140,9 @@ public class RenderHelper {
* @param colorComponents An array of R, G and B color components
* @param alpha The alpha of the lines
* @param lineWidth The width of the lines
+ * @param throughWalls Whether to render through walls or not
*/
- public static void renderLinesFromPoints(WorldRenderContext context, Vec3d[] points, float[] colorComponents, float alpha, float lineWidth) {
+ public static void renderLinesFromPoints(WorldRenderContext context, Vec3d[] points, float[] colorComponents, float alpha, float lineWidth, boolean throughWalls) {
Vec3d camera = context.camera().getPos();
MatrixStack matrices = context.matrixStack();
@@ -163,6 +164,7 @@ public class RenderHelper {
RenderSystem.defaultBlendFunc();
RenderSystem.disableCull();
RenderSystem.enableDepthTest();
+ RenderSystem.depthFunc(throughWalls ? GL11.GL_ALWAYS : GL11.GL_LEQUAL);
buffer.begin(DrawMode.LINE_STRIP, VertexFormats.LINES);
@@ -182,6 +184,7 @@ public class RenderHelper {
GL11.glDisable(GL11.GL_LINE_SMOOTH);
RenderSystem.lineWidth(1f);
RenderSystem.enableCull();
+ RenderSystem.depthFunc(GL11.GL_LEQUAL);
}
public static void renderQuad(WorldRenderContext context, Vec3d[] points, float[] colorComponents, float alpha, boolean throughWalls) {
diff --git a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java
index 3a1d364f..2f9c9f63 100644
--- a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java
+++ b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java
@@ -19,6 +19,10 @@ public class Waypoint {
final boolean throughWalls;
private boolean shouldRender;
+ public Waypoint(BlockPos pos, Type type, float[] colorComponents) {
+ this(pos, type, colorComponents, DEFAULT_HIGHLIGHT_ALPHA);
+ }
+
public Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents) {
this(pos, typeSupplier, colorComponents, DEFAULT_HIGHLIGHT_ALPHA, DEFAULT_LINE_WIDTH);
}
@@ -62,6 +66,10 @@ public class Waypoint {
this.shouldRender = true;
}
+ public void toggle() {
+ this.shouldRender = !this.shouldRender;
+ }
+
protected float[] getColorComponents() {
return colorComponents;
}