aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/hysky/skyblocker')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java13
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java18
-rw-r--r--src/main/java/de/hysky/skyblocker/events/DungeonEvents.java31
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java)34
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java)22
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java58
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/ThreeWeirdos.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java)2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java)21
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Trivia.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java)2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DebugRoom.java60
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java)145
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java182
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java10
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Tickable.java5
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/Renderable.java7
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java10
21 files changed, 488 insertions, 152 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index ad5e442f..9ce0df8d 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -5,8 +5,14 @@ import com.google.gson.GsonBuilder;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.skyblock.*;
-import de.hysky.skyblocker.skyblock.dungeon.*;
-import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
+import de.hysky.skyblocker.skyblock.dungeon.DungeonMap;
+import de.hysky.skyblocker.skyblock.dungeon.FireFreezeStaffTimer;
+import de.hysky.skyblocker.skyblock.dungeon.GuardianHealth;
+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.secrets.DungeonManager;
import de.hysky.skyblocker.skyblock.dungeon.secrets.SecretsTracker;
import de.hysky.skyblocker.skyblock.dwarven.DwarvenHud;
import de.hysky.skyblocker.skyblock.item.*;
@@ -96,7 +102,7 @@ public class SkyblockerMod implements ClientModInitializer {
FishingHelper.init();
TabHud.init();
DungeonMap.init();
- DungeonSecrets.init();
+ DungeonManager.init();
DungeonBlaze.init();
ChestValue.init();
FireFreezeStaffTimer.init();
@@ -124,7 +130,6 @@ public class SkyblockerMod implements ClientModInitializer {
statusBarTracker.init();
Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20);
Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 200);
- Scheduler.INSTANCE.scheduleCyclic(TicTacToe::tick, 4);
Scheduler.INSTANCE.scheduleCyclic(LividColor::update, 10);
Scheduler.INSTANCE.scheduleCyclic(BackpackPreview::tick, 50);
Scheduler.INSTANCE.scheduleCyclic(DwarvenHud::update, 40);
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index 8604913c..25d0c0bc 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -644,10 +644,10 @@ public class SkyblockerConfig {
public static class SecretWaypoints {
@SerialEntry
- public boolean enableSecretWaypoints = true;
+ public boolean enableRoomMatching = true;
@SerialEntry
- public boolean noInitSecretWaypoints = false;
+ public boolean enableSecretWaypoints = true;
@SerialEntry
public Waypoint.Type waypointType = Waypoint.Type.WAYPOINT;
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 3d304487..79e0b00c 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/DungeonsCategory.java
@@ -28,21 +28,21 @@ public class DungeonsCategory {
.name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints"))
.collapsed(true)
.option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableRoomMatching"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableRoomMatching.@Tooltip")))
+ .binding(defaults.locations.dungeons.secretWaypoints.enableRoomMatching,
+ () -> config.locations.dungeons.secretWaypoints.enableRoomMatching,
+ newValue -> config.locations.dungeons.secretWaypoints.enableRoomMatching = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .flag(OptionFlag.GAME_RESTART)
+ .build())
+ .option(Option.<Boolean>createBuilder()
.name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.enableSecretWaypoints"))
.binding(defaults.locations.dungeons.secretWaypoints.enableSecretWaypoints,
() -> config.locations.dungeons.secretWaypoints.enableSecretWaypoints,
newValue -> config.locations.dungeons.secretWaypoints.enableSecretWaypoints = newValue)
.controller(ConfigUtils::createBooleanController)
.build())
- .option(Option.<Boolean>createBuilder()
- .name(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.noInitSecretWaypoints"))
- .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.locations.dungeons.secretWaypoints.noInitSecretWaypoints.@Tooltip")))
- .binding(defaults.locations.dungeons.secretWaypoints.noInitSecretWaypoints,
- () -> config.locations.dungeons.secretWaypoints.noInitSecretWaypoints,
- newValue -> config.locations.dungeons.secretWaypoints.noInitSecretWaypoints = newValue)
- .controller(ConfigUtils::createBooleanController)
- .flag(OptionFlag.GAME_RESTART)
- .build())
.option(Option.<Type>createBuilder()
.name(Text.translatable("text.autoconfig.skyblocker.option.general.waypoints.waypointType"))
.description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.waypoints.waypointType.@Tooltip")))
diff --git a/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java b/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java
new file mode 100644
index 00000000..bf7ba2b2
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/events/DungeonEvents.java
@@ -0,0 +1,31 @@
+package de.hysky.skyblocker.events;
+
+import de.hysky.skyblocker.skyblock.dungeon.secrets.Room;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+import net.fabricmc.fabric.api.event.Event;
+import net.fabricmc.fabric.api.event.EventFactory;
+
+public class DungeonEvents {
+ // TODO Some rooms such as creeper beam and water board does not get matched
+ public static final Event<RoomMatched> PUZZLE_MATCHED = EventFactory.createArrayBacked(RoomMatched.class, callbacks -> room -> {
+ for (RoomMatched callback : callbacks) {
+ callback.onRoomMatched(room);
+ }
+ });
+
+ public static final Event<RoomMatched> ROOM_MATCHED = EventFactory.createArrayBacked(RoomMatched.class, callbacks -> room -> {
+ for (RoomMatched callback : callbacks) {
+ callback.onRoomMatched(room);
+ }
+ if (room.getType() == Room.Type.PUZZLE) {
+ PUZZLE_MATCHED.invoker().onRoomMatched(room);
+ }
+ });
+
+ @Environment(EnvType.CLIENT)
+ @FunctionalInterface
+ public interface RoomMatched {
+ void onRoomMatched(Room room);
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java
index dc2fa673..fa97e546 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/BatEntityMixin.java
@@ -1,6 +1,6 @@
package de.hysky.skyblocker.mixin;
-import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.entity.passive.BatEntity;
@@ -16,6 +16,6 @@ public abstract class BatEntityMixin extends AmbientEntity {
@Override
public void onRemoved() {
super.onRemoved();
- DungeonSecrets.onBatRemoved(this);
+ DungeonManager.onBatRemoved(this);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
index f177d2f8..7f1320c8 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayNetworkHandlerMixin.java
@@ -3,8 +3,8 @@ package de.hysky.skyblocker.mixin;
import com.llamalad7.mixinextras.injector.WrapWithCondition;
import com.llamalad7.mixinextras.sugar.Local;
import de.hysky.skyblocker.skyblock.FishingHelper;
+import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager;
import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual;
-import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonSecrets;
import de.hysky.skyblocker.utils.Utils;
import dev.cbyrne.betterinject.annotations.Inject;
import net.minecraft.client.MinecraftClient;
@@ -28,7 +28,7 @@ public abstract class ClientPlayNetworkHandlerMixin {
@ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "STORE", ordinal = 0))
private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity, @Local LivingEntity collector) {
- DungeonSecrets.onItemPickup(itemEntity, collector, collector == MinecraftClient.getInstance().player);
+ DungeonManager.onItemPickup(itemEntity, collector, collector == MinecraftClient.getInstance().player);
return itemEntity;
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java
index 5c7a01f9..8de1e3fe 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/CreeperBeams.java
@@ -1,13 +1,10 @@
-package de.hysky.skyblocker.skyblock.dungeon;
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.render.RenderHelper;
-import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.objects.ObjectDoublePair;
-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.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
@@ -27,8 +24,7 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
-public class CreeperBeams {
-
+public class CreeperBeams extends DungeonPuzzle {
private static final Logger LOGGER = LoggerFactory.getLogger(CreeperBeams.class.getName());
private static final float[][] COLORS = {
@@ -41,27 +37,30 @@ public class CreeperBeams {
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 ArrayList<Beam> beams = new ArrayList<>();
private static BlockPos base = null;
- private static boolean solved = false;
+
+ private CreeperBeams(String puzzleName, String... roomName) {
+ super(puzzleName, roomName);
+ }
public static void init() {
- Scheduler.INSTANCE.scheduleCyclic(CreeperBeams::update, 20);
- WorldRenderEvents.BEFORE_DEBUG_RENDER.register(CreeperBeams::render);
- ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset()));
}
- private static void reset() {
+ @Override
+ public void reset() {
+ super.reset();
beams.clear();
base = null;
- solved = false;
}
- private static void update() {
+ @Override
+ public void tick() {
// don't do anything if the room is solved
- if (solved) {
+ if (!shouldSolve()) {
return;
}
@@ -90,7 +89,7 @@ public class CreeperBeams {
// check if the room is solved
if (!isTarget(world, base)) {
- solved = true;
+ reset();
}
}
@@ -176,10 +175,11 @@ public class CreeperBeams {
return result;
}
- private static void render(WorldRenderContext wrc) {
+ @Override
+ public void render(WorldRenderContext wrc) {
// don't render if solved or disabled
- if (solved || !SkyblockerConfigManager.get().locations.dungeons.creeperSolver) {
+ if (!shouldSolve() || !SkyblockerConfigManager.get().locations.dungeons.creeperSolver) {
return;
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java
index f49a2f2e..5774eaef 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonBlaze.java
@@ -1,12 +1,10 @@
-package de.hysky.skyblocker.skyblock.dungeon;
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.render.RenderHelper;
-import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
@@ -24,25 +22,32 @@ import java.util.List;
/**
* This class provides functionality to render outlines around Blaze entities
*/
-public class DungeonBlaze {
+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 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);
+ }
+
public static void init() {
- Scheduler.INSTANCE.scheduleCyclic(DungeonBlaze::update, 4);
- WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer);
}
/**
* Updates the state of Blaze entities and triggers the rendering process if necessary.
*/
- public static void update() {
+ @Override
+ public void tick() {
+ if (!shouldSolve()) {
+ return;
+ }
ClientWorld world = MinecraftClient.getInstance().world;
ClientPlayerEntity player = MinecraftClient.getInstance().player;
if (world == null || player == null || !Utils.isInDungeons()) return;
@@ -104,7 +109,8 @@ public class DungeonBlaze {
*
* @param wrc The WorldRenderContext used for rendering.
*/
- public static void blazeRenderer(WorldRenderContext wrc) {
+ @Override
+ public void render(WorldRenderContext wrc) {
try {
if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfigManager.get().locations.dungeons.blazeSolver) {
if (highestBlaze.getY() < 69) {
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
new file mode 100644
index 00000000..04446e60
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/DungeonPuzzle.java
@@ -0,0 +1,58 @@
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
+
+import com.mojang.brigadier.Command;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.events.DungeonEvents;
+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.Tickable;
+import de.hysky.skyblocker.utils.render.Renderable;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
+import org.jetbrains.annotations.NotNull;
+
+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;
+ @NotNull
+ private final Set<String> roomNames;
+ private boolean shouldSolve;
+
+ public DungeonPuzzle(String puzzleName, String... roomName) {
+ this(puzzleName, Set.of(roomName));
+ }
+
+ public DungeonPuzzle(String puzzleName, @NotNull Set<String> roomNames) {
+ this.puzzleName = puzzleName;
+ this.roomNames = roomNames;
+ DungeonEvents.PUZZLE_MATCHED.register(room -> {
+ if (roomNames.contains(room.getName())) {
+ room.addSubProcess(this);
+ shouldSolve = true;
+ }
+ });
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("solvePuzzle").then(literal(puzzleName).executes(context -> {
+ Room currentRoom = DungeonManager.getCurrentRoom();
+ if (currentRoom != null) {
+ 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());
+ }
+
+ public boolean shouldSolve() {
+ return shouldSolve;
+ }
+
+ public void reset() {
+ shouldSolve = false;
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/ThreeWeirdos.java
index e1ab2fa8..c5e55f93 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/ThreeWeirdos.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/ThreeWeirdos.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.dungeon;
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.chat.ChatFilterResult;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java
index 7f249e7d..90028a4f 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/TicTacToe.java
@@ -1,11 +1,10 @@
-package de.hysky.skyblocker.skyblock.dungeon;
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.render.RenderHelper;
import de.hysky.skyblocker.utils.tictactoe.TicTacToeUtils;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
-import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
@@ -25,16 +24,25 @@ import java.util.List;
/**
* Thanks to Danker for a reference implementation!
*/
-public class TicTacToe {
+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 Box nextBestMoveToMake = null;
+ private TicTacToe(String puzzleName, String... roomName) {
+ super(puzzleName, roomName);
+ }
+
public static void init() {
- WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer);
}
- public static void tick() {
+ @Override
+ public void tick() {
+ if (!shouldSolve()) {
+ return;
+ }
+
MinecraftClient client = MinecraftClient.getInstance();
ClientWorld world = client.world;
ClientPlayerEntity player = client.player;
@@ -124,7 +132,8 @@ public class TicTacToe {
}
}
- private static void solutionRenderer(WorldRenderContext context) {
+ @Override
+ public void render(WorldRenderContext context) {
try {
if (SkyblockerConfigManager.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) {
RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5, false);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Trivia.java
index 21bbdce0..0f73457c 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/Trivia.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/puzzle/Trivia.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.dungeon;
+package de.hysky.skyblocker.skyblock.dungeon.puzzle;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.waypoint.FairySouls;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DebugRoom.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DebugRoom.java
new file mode 100644
index 00000000..931d1d69
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DebugRoom.java
@@ -0,0 +1,60 @@
+package de.hysky.skyblocker.skyblock.dungeon.secrets;
+
+import de.hysky.skyblocker.utils.waypoint.Waypoint;
+import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
+import it.unimi.dsi.fastutil.ints.IntSortedSet;
+import it.unimi.dsi.fastutil.ints.IntSortedSets;
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+import net.minecraft.client.world.ClientWorld;
+import net.minecraft.registry.Registries;
+import net.minecraft.util.math.BlockPos;
+import org.apache.commons.lang3.tuple.MutableTriple;
+import org.joml.Vector2ic;
+
+import java.util.*;
+
+public class DebugRoom extends Room {
+ private final List<Waypoint> checkedBlocks = Collections.synchronizedList(new ArrayList<>());
+
+ public DebugRoom(Type type, Vector2ic... physicalPositions) {
+ super(type, physicalPositions);
+ }
+
+ public static DebugRoom ofSinglePossibleRoom(Type type, Vector2ic physicalPositions, String roomName, int[] roomData, Direction direction) {
+ return ofSinglePossibleRoom(type, new Vector2ic[]{physicalPositions}, roomName, roomData, direction);
+ }
+
+ public static DebugRoom ofSinglePossibleRoom(Type type, Vector2ic[] physicalPositions, String roomName, int[] roomData, Direction direction) {
+ DebugRoom room = new DebugRoom(type, physicalPositions);
+ IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(room.segments.stream().mapToInt(Vector2ic::x).toArray()));
+ IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(room.segments.stream().mapToInt(Vector2ic::y).toArray()));
+ room.roomsData = Map.of(roomName, roomData);
+ room.possibleRooms = List.of(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), List.of(roomName)));
+ return room;
+ }
+
+ @Override
+ protected boolean checkBlock(ClientWorld world, BlockPos pos) {
+ byte id = DungeonManager.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString());
+ if (id == 0) {
+ return false;
+ }
+ for (MutableTriple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
+ int block = posIdToInt(DungeonMapUtils.actualToRelative(directionRooms.getLeft(), directionRooms.getMiddle(), pos), id);
+ for (String room : directionRooms.getRight()) {
+ checkedBlocks.add(new Waypoint(pos, SecretWaypoint.TYPE_SUPPLIER, Arrays.binarySearch(roomsData.get(room), block) >= 0 ? Room.GREEN_COLOR_COMPONENTS : Room.RED_COLOR_COMPONENTS));
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void render(WorldRenderContext context) {
+ super.render(context);
+ synchronized (checkedBlocks) {
+ for (Waypoint checkedBlock : checkedBlocks) {
+ checkedBlock.render(context);
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
index 7f401fdb..70a0fd8c 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonManager.java
@@ -7,12 +7,14 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
-import com.mojang.brigadier.builder.ArgumentBuilder;
+import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.serialization.JsonOps;
import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.debug.Debug;
import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
@@ -27,8 +29,9 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
+import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.BlockPosArgumentType;
import net.minecraft.command.argument.PosArgument;
import net.minecraft.command.argument.TextArgumentType;
@@ -37,10 +40,12 @@ import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.entity.passive.BatEntity;
+import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FilledMapItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.map.MapState;
+import net.minecraft.registry.Registry;
import net.minecraft.resource.Resource;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
@@ -75,15 +80,15 @@ import java.util.zip.InflaterInputStream;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
-public class DungeonSecrets {
- protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class);
+public class DungeonManager {
+ protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonManager.class);
private static final String DUNGEONS_PATH = "dungeons";
private static final Path CUSTOM_WAYPOINTS_DIR = SkyblockerMod.CONFIG_DIR.resolve("custom_secret_waypoints.json");
private static final Pattern KEY_FOUND = Pattern.compile("^(?:\\[.+] )?(?<name>\\w+) has obtained (?<type>Wither|Blood) Key!$");
/**
* Maps the block identifier string to a custom numeric block id used in dungeon rooms data.
*
- * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft.
+ * @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(
@@ -151,11 +156,13 @@ public class DungeonSecrets {
@SuppressWarnings("unused")
public static JsonObject getRoomMetadata(String room) {
- return roomsJson.get(room).getAsJsonObject();
+ JsonElement value = roomsJson.get(room);
+ return value != null ? value.getAsJsonObject() : null;
}
public static JsonArray getRoomWaypoints(String room) {
- return waypointsJson.get(room).getAsJsonArray();
+ JsonElement value = waypointsJson.get(room);
+ return value != null ? value.getAsJsonArray() : null;
}
/**
@@ -190,35 +197,54 @@ public class DungeonSecrets {
return customWaypoints.remove(room, pos);
}
+ public static Room getCurrentRoom() {
+ return currentRoom;
+ }
+
/**
* Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}.
* Use {@link #isRoomsLoaded()} to check for completion of loading.
*/
public static void init() {
- if (SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.noInitSecretWaypoints) {
+ if (!SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableRoomMatching) {
return;
}
// Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set
- CompletableFuture.runAsync(DungeonSecrets::load, MinecraftClient.getInstance()).exceptionally(e -> {
+ CompletableFuture.runAsync(DungeonManager::load, MinecraftClient.getInstance()).exceptionally(e -> {
LOGGER.error("[Skyblocker Dungeon Secrets] Failed to load dungeon secrets", e);
return null;
});
- ClientLifecycleEvents.CLIENT_STOPPING.register(DungeonSecrets::saveCustomWaypoints);
- Scheduler.INSTANCE.scheduleCyclic(DungeonSecrets::update, 10);
- WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render);
- ClientReceiveMessageEvents.GAME.register(DungeonSecrets::onChatMessage);
- ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonSecrets::onChatMessage);
+ ClientLifecycleEvents.CLIENT_STOPPING.register(DungeonManager::saveCustomWaypoints);
+ Scheduler.INSTANCE.scheduleCyclic(DungeonManager::update, 5);
+ WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonManager::render);
+ ClientReceiveMessageEvents.GAME.register(DungeonManager::onChatMessage);
+ ClientReceiveMessageEvents.GAME_CANCELED.register(DungeonManager::onChatMessage);
UseBlockCallback.EVENT.register((player, world, hand, hitResult) -> onUseBlock(world, hitResult));
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("secrets")
.then(literal("markAsFound").then(markSecretsCommand(true)))
.then(literal("markAsMissing").then(markSecretsCommand(false)))
- .then(literal("getRelativePos").executes(DungeonSecrets::getRelativePos))
- .then(literal("getRelativeTargetPos").executes(DungeonSecrets::getRelativeTargetPos))
+ .then(literal("getRelativePos").executes(DungeonManager::getRelativePos))
+ .then(literal("getRelativeTargetPos").executes(DungeonManager::getRelativeTargetPos))
.then(literal("addWaypoint").then(addCustomWaypointCommand(false)))
.then(literal("addWaypointRelatively").then(addCustomWaypointCommand(true)))
.then(literal("removeWaypoint").then(removeCustomWaypointCommand(false)))
.then(literal("removeWaypointRelatively").then(removeCustomWaypointCommand(true)))
))));
+ if (Debug.debugEnabled()) {
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> dispatcher.register(literal(SkyblockerMod.NAMESPACE).then(literal("dungeons").then(literal("secrets")
+ .then(literal("matchAgainst").then(matchAgainstCommand()))
+ .then(literal("clearSubProcesses").executes(context -> {
+ if (currentRoom != null) {
+ currentRoom.tickables.clear();
+ currentRoom.renderables.clear();
+ context.getSource().sendFeedback(Constants.PREFIX.get().append("§rCleared sub processes 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()));
}
@@ -304,7 +330,7 @@ public class DungeonSecrets {
SkyblockerMod.GSON.fromJson(reader, JsonObject.class).asMap().forEach((room, jsonElement) -> map.put(room.toLowerCase().replaceAll(" ", "-"), jsonElement));
}
- private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, Integer>> markSecretsCommand(boolean found) {
+ private static RequiredArgumentBuilder<FabricClientCommandSource, Integer> markSecretsCommand(boolean found) {
return argument("secretIndex", IntegerArgumentType.integer()).executes(context -> {
int secretIndex = IntegerArgumentType.getInteger(context, "secretIndex");
if (markSecrets(secretIndex, found)) {
@@ -333,14 +359,14 @@ public class DungeonSecrets {
Room room = getRoomAtPhysical(pos);
if (isRoomMatched(room)) {
BlockPos relativePos = currentRoom.actualToRelative(pos);
- source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.posMessage", currentRoom.getName(), relativePos.getX(), relativePos.getY(), relativePos.getZ())));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.posMessage", currentRoom.getName(), currentRoom.getDirection().asString(), relativePos.getX(), relativePos.getY(), relativePos.getZ())));
} else {
source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.notMatched")));
}
return Command.SINGLE_SUCCESS;
}
- private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, PosArgument>> addCustomWaypointCommand(boolean relative) {
+ private static RequiredArgumentBuilder<FabricClientCommandSource, PosArgument> addCustomWaypointCommand(boolean relative) {
return argument("pos", BlockPosArgumentType.blockPos())
.then(argument("secretIndex", IntegerArgumentType.integer())
.then(argument("category", SecretWaypoint.Category.CategoryArgumentType.category())
@@ -372,7 +398,7 @@ public class DungeonSecrets {
return Command.SINGLE_SUCCESS;
}
- private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, PosArgument>> removeCustomWaypointCommand(boolean relative) {
+ private static RequiredArgumentBuilder<FabricClientCommandSource, PosArgument> removeCustomWaypointCommand(boolean relative) {
return argument("pos", BlockPosArgumentType.blockPos())
.executes(context -> {
// TODO Less hacky way with custom ClientBlockPosArgumentType
@@ -400,6 +426,61 @@ public class DungeonSecrets {
return Command.SINGLE_SUCCESS;
}
+ private static RequiredArgumentBuilder<FabricClientCommandSource, String> matchAgainstCommand() {
+ return argument("room", StringArgumentType.string()).suggests((context, builder) -> CommandSource.suggestMatching(ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).map(Map::keySet).flatMap(Collection::stream), builder)).then(argument("direction", Room.Direction.DirectionArgumentType.direction()).executes(context -> {
+ if (physicalEntrancePos == null || mapEntrancePos == null || mapRoomSize == 0) {
+ context.getSource().sendError(Constants.PREFIX.get().append("§cYou are not in a dungeon."));
+ return Command.SINGLE_SUCCESS;
+ }
+ MinecraftClient client = MinecraftClient.getInstance();
+ if (client.player == null || client.world == null) {
+ context.getSource().sendError(Constants.PREFIX.get().append("§cFailed to get player or world."));
+ return Command.SINGLE_SUCCESS;
+ }
+ ItemStack stack = client.player.getInventory().main.get(8);
+ if (!stack.isOf(Items.FILLED_MAP)) {
+ context.getSource().sendError(Constants.PREFIX.get().append("§cFailed to get dungeon map."));
+ return Command.SINGLE_SUCCESS;
+ }
+ MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world);
+ if (map == null) {
+ context.getSource().sendError(Constants.PREFIX.get().append("§cFailed to get dungeon map state."));
+ return Command.SINGLE_SUCCESS;
+ }
+
+ String roomName = StringArgumentType.getString(context, "room");
+ Room.Direction direction = Room.Direction.DirectionArgumentType.getDirection(context, "direction");
+
+ Room room = newDebugRoom(roomName, direction, client.player, map);
+ if (room == null) {
+ context.getSource().sendError(Constants.PREFIX.get().append("§cFailed to find room with name " + roomName + "."));
+ return Command.SINGLE_SUCCESS;
+ }
+ if (currentRoom != null) {
+ currentRoom.addSubProcess(room);
+ context.getSource().sendFeedback(Constants.PREFIX.get().append("§rMatching room " + roomName + " with direction " + direction + " against current room."));
+ } else {
+ context.getSource().sendError(Constants.PREFIX.get().append("§cCurrent room is null."));
+ }
+
+ return Command.SINGLE_SUCCESS;
+ }));
+ }
+
+ @Nullable
+ private static Room newDebugRoom(String roomName, Room.Direction direction, PlayerEntity player, MapState map) {
+ Room room = null;
+ int[] roomData;
+ if ((roomData = ROOMS_DATA.get("catacombs").get(Room.Shape.PUZZLE.shape).get(roomName)) != null) {
+ room = DebugRoom.ofSinglePossibleRoom(Room.Type.PUZZLE, DungeonMapUtils.getPhysicalRoomPos(player.getPos()), roomName, roomData, direction);
+ } else if ((roomData = ROOMS_DATA.get("catacombs").get(Room.Shape.TRAP.shape).get(roomName)) != null) {
+ room = DebugRoom.ofSinglePossibleRoom(Room.Type.TRAP, DungeonMapUtils.getPhysicalRoomPos(player.getPos()), roomName, roomData, direction);
+ } else if ((roomData = ROOMS_DATA.get("catacombs").values().stream().map(Map::entrySet).flatMap(Collection::stream).filter(entry -> entry.getKey().equals(roomName)).findAny().map(Map.Entry::getValue).orElse(null)) != null) {
+ room = DebugRoom.ofSinglePossibleRoom(Room.Type.ROOM, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, DungeonMapUtils.getMapRoomPos(map, mapEntrancePos, mapRoomSize), mapRoomSize, Room.Type.ROOM.color)), roomName, roomData, direction);
+ }
+ return room;
+ }
+
/**
* Updates the dungeon. The general idea is similar to the Dungeon Rooms Mod.
* <p></p>
@@ -421,14 +502,11 @@ public class DungeonSecrets {
* <li> Create a new room. </li>
* </ul>
* <li> Sets {@link #currentRoom} to the current room, either created from the previous step or from {@link #rooms}. </li>
- * <li> Calls {@link Room#update()} on {@link #currentRoom}. </li>
+ * <li> Calls {@link Room#tick()} on {@link #currentRoom}. </li>
* </ul>
*/
@SuppressWarnings("JavadocReference")
private static void update() {
- if (!SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints) {
- return;
- }
if (!Utils.isInDungeons()) {
if (mapEntrancePos != null) {
reset();
@@ -436,16 +514,15 @@ public class DungeonSecrets {
return;
}
MinecraftClient client = MinecraftClient.getInstance();
- ClientPlayerEntity player = client.player;
- if (player == null || client.world == null) {
+ if (client.player == null || client.world == null) {
return;
}
if (physicalEntrancePos == null) {
- Vec3d playerPos = player.getPos();
+ Vec3d playerPos = client.player.getPos();
physicalEntrancePos = DungeonMapUtils.getPhysicalRoomPos(playerPos);
currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos);
}
- ItemStack stack = player.getInventory().main.get(8);
+ ItemStack stack = client.player.getInventory().main.get(8);
if (!stack.isOf(Items.FILLED_MAP)) {
return;
}
@@ -477,9 +554,13 @@ public class DungeonSecrets {
}
}
if (room != null && currentRoom != room) {
+ if (currentRoom != null && room.getType() == Room.Type.FAIRY) {
+ currentRoom.nextRoom = room;
+ room.keyFound = currentRoom.keyFound;
+ }
currentRoom = room;
}
- currentRoom.update();
+ currentRoom.tick();
}
/**
@@ -663,12 +744,12 @@ public class DungeonSecrets {
}
/**
- * Checks if the player is in a dungeon and {@link de.hysky.skyblocker.config.SkyblockerConfig.Dungeons#secretWaypoints Secret Waypoints} is enabled.
+ * Checks if {@link SkyblockerConfig.SecretWaypoints#enableRoomMatching room matching} is enabled and the player is in a dungeon.
*
- * @return whether dungeon secrets should be processed
+ * @return whether room matching and dungeon secrets should be processed
*/
private static boolean shouldProcess() {
- return SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints && Utils.isInDungeons();
+ return SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableRoomMatching && Utils.isInDungeons();
}
/**
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
index 01f2c9fc..b12bba62 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
@@ -130,7 +130,7 @@ public class DungeonMapUtils {
return null;
}
Vector2ic offset = new Vector2i(mapEntrancePos.x() % mapRoomSizeWithGap, mapEntrancePos.y() % mapRoomSizeWithGap);
- return mapPos.add(2, 2).sub(offset).sub(mapPos.x() % mapRoomSizeWithGap, mapPos.y() % mapRoomSizeWithGap).add(offset);
+ return mapPos.add(2, 2).sub(offset).sub(Math.floorMod(mapPos.x(), mapRoomSizeWithGap), Math.floorMod(mapPos.y(), mapRoomSizeWithGap)).add(offset);
}
/**
@@ -271,7 +271,7 @@ public class DungeonMapUtils {
queue.add(newMapPos);
}
}
- DungeonSecrets.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray()));
+ DungeonManager.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray()));
return segments.toArray(Vector2ic[]::new);
}
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 7797513f..4857e8fe 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
@@ -2,13 +2,18 @@ package de.hysky.skyblocker.skyblock.dungeon.secrets;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
+import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.context.CommandContext;
+import com.mojang.serialization.Codec;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.events.DungeonEvents;
import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.Tickable;
import de.hysky.skyblocker.utils.render.RenderHelper;
+import de.hysky.skyblocker.utils.render.Renderable;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
@@ -22,11 +27,13 @@ import net.minecraft.block.MapColor;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
+import net.minecraft.command.argument.EnumArgumentType;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.AmbientEntity;
import net.minecraft.registry.Registries;
import net.minecraft.text.Text;
+import net.minecraft.util.StringIdentifiable;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
@@ -44,16 +51,16 @@ import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-public class Room {
+public class Room implements Tickable, Renderable {
private static final Pattern SECRET_INDEX = Pattern.compile("^(\\d+)");
private static final Pattern SECRETS = Pattern.compile("§7(\\d{1,2})/(\\d{1,2}) Secrets");
private static final Vec3d DOOR_SIZE = new Vec3d(3, 4, 3);
- private static final float[] RED_COLOR_COMPONENTS = {1, 0, 0};
- private static final float[] GREEN_COLOR_COMPONENTS = {0, 1, 0};
+ protected static final float[] RED_COLOR_COMPONENTS = {1, 0, 0};
+ protected static final float[] GREEN_COLOR_COMPONENTS = {0, 1, 0};
@NotNull
private final Type type;
@NotNull
- private final Set<Vector2ic> segments;
+ final Set<Vector2ic> segments;
/**
* The shape of the room. See {@link #getShape(IntSortedSet, IntSortedSet)}.
@@ -63,11 +70,11 @@ public class Room {
/**
* The room data containing all rooms for a specific dungeon and {@link #shape}.
*/
- private Map<String, int[]> roomsData;
+ protected Map<String, int[]> roomsData;
/**
* Contains all possible dungeon rooms for this room. The list is gradually shrunk by checking blocks until only one room is left.
*/
- private List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms;
+ protected List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms;
/**
* Contains all blocks that have been checked to prevent checking the same block multiple times.
*/
@@ -75,7 +82,7 @@ public class Room {
/**
* The task that is used to check blocks. This is used to ensure only one such task can run at a time.
*/
- private CompletableFuture<Void> findRoom;
+ protected CompletableFuture<Void> findRoom;
private int doubleCheckBlocks;
/**
* Represents the matching state of the room with the following possible values:
@@ -84,17 +91,24 @@ public class Room {
* <li>{@link MatchState#MATCHED} means that the room has a unique match ans has been double checked.</li>
* <li>{@link MatchState#FAILED} means that the room has been checked and there is no match.</li>
*/
- private MatchState matchState = MatchState.MATCHING;
+ protected MatchState matchState = MatchState.MATCHING;
private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints;
private String name;
private Direction direction;
private Vector2ic physicalCornerPos;
+ protected List<Tickable> tickables = new ArrayList<>();
+ protected List<Renderable> renderables = new ArrayList<>();
+ /**
+ * Stores the next room in the dungeon. Currently only used if the next room is the fairy room.
+ */
+ @Nullable
+ protected Room nextRoom;
@Nullable
private BlockPos doorPos;
@Nullable
private Box doorBox;
- private boolean keyFound;
+ protected boolean keyFound;
public Room(@NotNull Type type, @NotNull Vector2ic... physicalPositions) {
this.type = type;
@@ -102,7 +116,7 @@ public class Room {
IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
shape = getShape(segmentsX, segmentsY);
- roomsData = DungeonSecrets.ROOMS_DATA.getOrDefault("catacombs", Collections.emptyMap()).getOrDefault(shape.shape.toLowerCase(), Collections.emptyMap());
+ roomsData = DungeonManager.ROOMS_DATA.getOrDefault("catacombs", Collections.emptyMap()).getOrDefault(shape.shape.toLowerCase(), Collections.emptyMap());
possibleRooms = getPossibleRooms(segmentsX, segmentsY);
}
@@ -122,6 +136,13 @@ public class Room {
return name;
}
+ /**
+ * Not null if {@link #isMatched()}.
+ */
+ public Direction getDirection() {
+ return direction;
+ }
+
@Override
public String toString() {
return "Room{type=%s, segments=%s, shape=%s, matchState=%s, name=%s, direction=%s, physicalCornerPos=%s}".formatted(type, Arrays.toString(segments.toArray()), shape, matchState, name, direction, physicalCornerPos);
@@ -129,12 +150,16 @@ public class Room {
@NotNull
private Shape getShape(IntSortedSet segmentsX, IntSortedSet segmentsY) {
- return switch (segments.size()) {
- case 1 -> Shape.ONE_BY_ONE;
- case 2 -> Shape.ONE_BY_TWO;
- case 3 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.L_SHAPE : Shape.ONE_BY_THREE;
- case 4 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.TWO_BY_TWO : Shape.ONE_BY_FOUR;
- default -> throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray()));
+ return switch (type) {
+ case PUZZLE -> Shape.PUZZLE;
+ case TRAP -> Shape.TRAP;
+ default -> switch (segments.size()) {
+ case 1 -> Shape.ONE_BY_ONE;
+ case 2 -> Shape.ONE_BY_TWO;
+ case 3 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.L_SHAPE : Shape.ONE_BY_THREE;
+ case 4 -> segmentsX.size() == 2 && segmentsY.size() == 2 ? Shape.TWO_BY_TWO : Shape.ONE_BY_FOUR;
+ default -> throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray()));
+ };
};
}
@@ -150,7 +175,7 @@ public class Room {
@NotNull
private Direction[] getPossibleDirections(IntSortedSet segmentsX, IntSortedSet segmentsY) {
return switch (shape) {
- case ONE_BY_ONE, TWO_BY_TWO -> Direction.values();
+ case ONE_BY_ONE, TWO_BY_TWO, PUZZLE, TRAP -> Direction.values();
case ONE_BY_TWO, ONE_BY_THREE, ONE_BY_FOUR -> {
if (segmentsX.size() > 1 && segmentsY.size() == 1) {
yield new Direction[]{Direction.NW, Direction.SE};
@@ -186,7 +211,7 @@ public class Room {
}
/**
- * Adds a custom waypoint relative to this room to {@link DungeonSecrets#customWaypoints} and all existing instances of this room.
+ * Adds a custom waypoint relative to this room to {@link DungeonManager#customWaypoints} and all existing instances of this room.
*
* @param secretIndex the index of the secret waypoint
* @param category the category of the secret waypoint
@@ -196,8 +221,8 @@ public class Room {
@SuppressWarnings("JavadocReference")
private void addCustomWaypoint(int secretIndex, SecretWaypoint.Category category, Text waypointName, BlockPos pos) {
SecretWaypoint waypoint = new SecretWaypoint(secretIndex, category, waypointName, pos);
- DungeonSecrets.addCustomWaypoint(name, waypoint);
- DungeonSecrets.getRoomsStream().filter(r -> name.equals(r.getName())).forEach(r -> r.addCustomWaypoint(waypoint));
+ DungeonManager.addCustomWaypoint(name, waypoint);
+ DungeonManager.getRoomsStream().filter(r -> name.equals(r.getName())).forEach(r -> r.addCustomWaypoint(waypoint));
}
/**
@@ -223,7 +248,7 @@ public class Room {
}
/**
- * Removes a custom waypoint relative to this room from {@link DungeonSecrets#customWaypoints} and all existing instances of this room.
+ * Removes a custom waypoint relative to this room from {@link DungeonManager#customWaypoints} and all existing instances of this room.
*
* @param pos the position of the secret waypoint relative to this room
* @return the removed secret waypoint or {@code null} if there was no secret waypoint at the given position
@@ -231,9 +256,9 @@ public class Room {
@SuppressWarnings("JavadocReference")
@Nullable
private SecretWaypoint removeCustomWaypoint(BlockPos pos) {
- SecretWaypoint waypoint = DungeonSecrets.removeCustomWaypoint(name, pos);
+ SecretWaypoint waypoint = DungeonManager.removeCustomWaypoint(name, pos);
if (waypoint != null) {
- DungeonSecrets.getRoomsStream().filter(r -> name.equals(r.getName())).forEach(r -> r.removeCustomWaypoint(waypoint.secretIndex, pos));
+ DungeonManager.getRoomsStream().filter(r -> name.equals(r.getName())).forEach(r -> r.removeCustomWaypoint(waypoint.secretIndex, pos));
}
return waypoint;
}
@@ -249,6 +274,11 @@ public class Room {
secretWaypoints.remove(secretIndex, actualPos);
}
+ public <T extends Tickable & Renderable> void addSubProcess(T process) {
+ tickables.add(process);
+ renderables.add(process);
+ }
+
/**
* Updates the room.
* <p></p>
@@ -268,13 +298,18 @@ public class Room {
* </ul>
*/
@SuppressWarnings("JavadocReference")
- protected void update() {
+ @Override
+ public void tick() {
MinecraftClient client = MinecraftClient.getInstance();
ClientWorld world = client.world;
if (world == null) {
return;
}
+ for (Tickable tickable : tickables) {
+ tickable.tick();
+ }
+
// Wither and blood door
if (SkyblockerConfigManager.get().locations.dungeons.doorHighlight.enableDoorHighlight && doorPos == null) {
doorPos = DungeonMapUtils.getWitherBloodDoorPos(world, segments);
@@ -285,7 +320,7 @@ public class Room {
// Room scanning and matching
// Logical AND has higher precedence than logical OR
- if (!type.needsScanning() || matchState != MatchState.MATCHING && matchState != MatchState.DOUBLE_CHECKING || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) {
+ if (!type.needsScanning() || matchState != MatchState.MATCHING && matchState != MatchState.DOUBLE_CHECKING || !DungeonManager.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) {
return;
}
ClientPlayerEntity player = client.player;
@@ -299,7 +334,7 @@ public class Room {
}
}
}).exceptionally(e -> {
- DungeonSecrets.LOGGER.error("[Skyblocker Dungeon Secrets] Encountered an unknown exception while matching room {}", this, e);
+ DungeonManager.LOGGER.error("[Skyblocker Dungeon Secrets] Encountered an unknown exception while matching room {}", this, e);
return null;
});
}
@@ -318,7 +353,7 @@ public class Room {
* <p></p>
* This method:
* <ul>
- * <li> Checks if the block type is included in the dungeon rooms data. See {@link DungeonSecrets#NUMERIC_ID}. </li>
+ * <li> Checks if the block type is included in the dungeon rooms data. See {@link DungeonManager#NUMERIC_ID}. </li>
* <li> For each possible direction: </li>
* <ul>
* <li> Rotate and convert the position to a relative position. See {@link DungeonMapUtils#actualToRelative(Direction, Vector2ic, BlockPos)}. </li>
@@ -358,8 +393,8 @@ public class Room {
* @param pos the position of the block to check
* @return whether room matching should end. Either a match is found or there are no valid rooms left
*/
- private boolean checkBlock(ClientWorld world, BlockPos pos) {
- byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString());
+ protected boolean checkBlock(ClientWorld world, BlockPos pos) {
+ byte id = DungeonManager.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString());
if (id == 0) {
return false;
}
@@ -377,7 +412,8 @@ public class Room {
int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum();
if (matchingRoomsSize == 0) {
// If no rooms match, reset the fields and scan again after 50 ticks.
- DungeonSecrets.LOGGER.warn("[Skyblocker Dungeon Secrets] No dungeon room matched after checking {} block(s) including double checking {} block(s)", checkedBlocks.size(), doubleCheckBlocks);
+ matchState = MatchState.FAILED;
+ DungeonManager.LOGGER.warn("[Skyblocker Dungeon Secrets] No dungeon room matched after checking {} block(s) including double checking {} block(s)", checkedBlocks.size(), doubleCheckBlocks);
Scheduler.INSTANCE.schedule(() -> matchState = MatchState.MATCHING, 50);
reset();
return true;
@@ -388,18 +424,20 @@ public class Room {
name = directionRoom.getRight().get(0);
direction = directionRoom.getLeft();
physicalCornerPos = directionRoom.getMiddle();
- DungeonSecrets.LOGGER.info("[Skyblocker Dungeon Secrets] Room {} matched after checking {} block(s), starting double checking", name, checkedBlocks.size());
+ DungeonManager.LOGGER.info("[Skyblocker Dungeon Secrets] Room {} matched after checking {} block(s), starting double checking", name, checkedBlocks.size());
roomMatched();
return false;
} else if (matchState == MatchState.DOUBLE_CHECKING && ++doubleCheckBlocks >= 10) {
// If double-checked, set state to matched and discard the no longer needed fields.
- DungeonSecrets.LOGGER.info("[Skyblocker Dungeon Secrets] Room {} matched after checking {} block(s) including double checking {} block(s)", name, checkedBlocks.size(), doubleCheckBlocks);
+ matchState = MatchState.MATCHED;
+ DungeonEvents.ROOM_MATCHED.invoker().onRoomMatched(this);
+ DungeonManager.LOGGER.info("[Skyblocker Dungeon Secrets] Room {} confirmed after checking {} block(s) including double checking {} block(s)", name, checkedBlocks.size(), doubleCheckBlocks);
discard();
return true;
}
return false;
} else {
- DungeonSecrets.LOGGER.debug("[Skyblocker Dungeon Secrets] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size());
+ DungeonManager.LOGGER.debug("[Skyblocker Dungeon Secrets] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size());
return false;
}
}
@@ -411,12 +449,12 @@ public class Room {
* @param id the custom numeric block id
* @return the encoded integer
*/
- private int posIdToInt(BlockPos pos, byte id) {
+ protected int posIdToInt(BlockPos pos, byte id) {
return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id;
}
/**
- * Loads the secret waypoints for the room from {@link DungeonSecrets#waypointsJson} once it has been matched
+ * Loads the secret waypoints for the room from {@link DungeonManager#waypointsJson} once it has been matched
* and sets {@link #matchState} to {@link MatchState#DOUBLE_CHECKING}.
*
* @param directionRooms the direction, position, and name of the room
@@ -424,23 +462,25 @@ public class Room {
@SuppressWarnings("JavadocReference")
private void roomMatched() {
secretWaypoints = HashBasedTable.create();
- for (JsonElement waypointElement : DungeonSecrets.getRoomWaypoints(name)) {
- JsonObject waypoint = waypointElement.getAsJsonObject();
- String secretName = waypoint.get("secretName").getAsString();
- Matcher secretIndexMatcher = SECRET_INDEX.matcher(secretName);
- int secretIndex = secretIndexMatcher.find() ? Integer.parseInt(secretIndexMatcher.group(1)) : 0;
- BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint);
- secretWaypoints.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos));
+ JsonArray secretWaypointsJson = DungeonManager.getRoomWaypoints(name);
+ if (secretWaypointsJson != null) {
+ for (JsonElement waypointElement : secretWaypointsJson) {
+ JsonObject waypoint = waypointElement.getAsJsonObject();
+ String secretName = waypoint.get("secretName").getAsString();
+ Matcher secretIndexMatcher = SECRET_INDEX.matcher(secretName);
+ int secretIndex = secretIndexMatcher.find() ? Integer.parseInt(secretIndexMatcher.group(1)) : 0;
+ BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint);
+ secretWaypoints.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos));
+ }
}
- DungeonSecrets.getCustomWaypoints(name).values().forEach(this::addCustomWaypoint);
+ DungeonManager.getCustomWaypoints(name).values().forEach(this::addCustomWaypoint);
matchState = MatchState.DOUBLE_CHECKING;
}
/**
* Resets fields for another round of matching after room matching fails.
*/
- private void reset() {
- matchState = MatchState.FAILED;
+ protected void reset() {
IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
possibleRooms = getPossibleRooms(segmentsX, segmentsY);
@@ -457,7 +497,6 @@ public class Room {
* These fields are no longer needed and are discarded to save memory.
*/
private void discard() {
- matchState = MatchState.MATCHED;
roomsData = null;
possibleRooms = null;
checkedBlocks = null;
@@ -481,8 +520,13 @@ public class Room {
/**
* Calls {@link SecretWaypoint#render(WorldRenderContext)} on {@link #secretWaypoints all secret waypoints} and renders a highlight around the wither or blood door, if it exists.
*/
- protected void render(WorldRenderContext context) {
- if (isMatched()) {
+ @Override
+ public void render(WorldRenderContext context) {
+ for (Renderable renderable : renderables) {
+ renderable.render(context);
+ }
+
+ if (SkyblockerConfigManager.get().locations.dungeons.secretWaypoints.enableSecretWaypoints && isMatched()) {
for (SecretWaypoint secretWaypoint : secretWaypoints.values()) {
if (secretWaypoint.shouldRender()) {
secretWaypoint.render(context);
@@ -580,7 +624,7 @@ public class Room {
*/
private void onSecretFound(SecretWaypoint secretWaypoint, String msg, Object... args) {
secretWaypoints.row(secretWaypoint.secretIndex).values().forEach(SecretWaypoint::setFound);
- DungeonSecrets.LOGGER.info(msg, args);
+ DungeonManager.LOGGER.info(msg, args);
}
protected boolean markSecrets(int secretIndex, boolean found) {
@@ -594,6 +638,9 @@ public class Room {
}
protected void keyFound() {
+ if (nextRoom != null && nextRoom.type == Type.FAIRY) {
+ nextRoom.keyFound = true;
+ }
keyFound = true;
}
@@ -623,13 +670,15 @@ public class Room {
}
}
- private enum Shape {
+ protected enum Shape {
ONE_BY_ONE("1x1"),
ONE_BY_TWO("1x2"),
ONE_BY_THREE("1x3"),
ONE_BY_FOUR("1x4"),
L_SHAPE("L-shape"),
- TWO_BY_TWO("2x2");
+ TWO_BY_TWO("2x2"),
+ PUZZLE("puzzle"),
+ TRAP("trap");
final String shape;
Shape(String shape) {
@@ -642,11 +691,36 @@ public class Room {
}
}
- public enum Direction {
- NW, NE, SW, SE
+ public enum Direction implements StringIdentifiable {
+ NW("northwest"), NE("northeast"), SW("southwest"), SE("southeast");
+ private static final Codec<Direction> CODEC = StringIdentifiable.createCodec(Direction::values);
+ private final String name;
+
+ Direction(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String asString() {
+ return name;
+ }
+
+ static class DirectionArgumentType extends EnumArgumentType<Direction> {
+ DirectionArgumentType() {
+ super(CODEC, Direction::values);
+ }
+
+ static DirectionArgumentType direction() {
+ return new DirectionArgumentType();
+ }
+
+ static <S> Direction getDirection(CommandContext<S> context, String name) {
+ return context.getArgument(name, Direction.class);
+ }
+ }
}
- public enum MatchState {
+ protected enum MatchState {
MATCHING, DOUBLE_CHECKING, MATCHED, FAILED
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
index 75a0c20f..98ffa157 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java
@@ -29,7 +29,7 @@ import java.util.function.Supplier;
import java.util.function.ToDoubleFunction;
public class SecretWaypoint extends Waypoint {
- protected static final Logger LOGGER = LoggerFactory.getLogger(SecretWaypoint.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(SecretWaypoint.class);
public static final Codec<SecretWaypoint> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.INT.fieldOf("secretIndex").forGetter(secretWaypoint -> secretWaypoint.secretIndex),
Category.CODEC.fieldOf("category").forGetter(secretWaypoint -> secretWaypoint.category),
@@ -39,7 +39,7 @@ public class SecretWaypoint extends Waypoint {
public static final Codec<List<SecretWaypoint>> LIST_CODEC = CODEC.listOf();
static final List<String> SECRET_ITEMS = List.of("Decoy", "Defuse Kit", "Dungeon Chest Key", "Healing VIII", "Inflatable Jerry", "Spirit Leap", "Training Weights", "Trap", "Treasure Talisman");
private static final Supplier<SkyblockerConfig.SecretWaypoints> CONFIG = () -> SkyblockerConfigManager.get().locations.dungeons.secretWaypoints;
- private static final Supplier<Type> TYPE_SUPPLIER = () -> CONFIG.get().waypointType;
+ static final Supplier<Type> TYPE_SUPPLIER = () -> CONFIG.get().waypointType;
final int secretIndex;
final Category category;
final Text name;
@@ -173,15 +173,15 @@ public class SecretWaypoint extends Waypoint {
}
static class CategoryArgumentType extends EnumArgumentType<Category> {
- public CategoryArgumentType() {
+ CategoryArgumentType() {
super(Category.CODEC, Category::values);
}
- public static CategoryArgumentType category() {
+ static CategoryArgumentType category() {
return new CategoryArgumentType();
}
- public static <S> Category getCategory(CommandContext<S> context, String name) {
+ static <S> Category getCategory(CommandContext<S> context, String name) {
return context.getArgument(name, Category.class);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Tickable.java b/src/main/java/de/hysky/skyblocker/utils/Tickable.java
new file mode 100644
index 00000000..9b7b2e3f
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/Tickable.java
@@ -0,0 +1,5 @@
+package de.hysky.skyblocker.utils;
+
+public interface Tickable {
+ void tick();
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java
index 2c75ef0a..42f890b7 100644
--- a/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java
+++ b/src/main/java/de/hysky/skyblocker/utils/chat/ChatMessageListener.java
@@ -5,8 +5,8 @@ import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.skyblock.barn.HungryHiker;
import de.hysky.skyblocker.skyblock.barn.TreasureHunter;
import de.hysky.skyblocker.skyblock.dungeon.Reparty;
-import de.hysky.skyblocker.skyblock.dungeon.ThreeWeirdos;
-import de.hysky.skyblocker.skyblock.dungeon.Trivia;
+import de.hysky.skyblocker.skyblock.dungeon.puzzle.ThreeWeirdos;
+import de.hysky.skyblocker.skyblock.dungeon.puzzle.Trivia;
import de.hysky.skyblocker.skyblock.dwarven.Fetchur;
import de.hysky.skyblocker.skyblock.dwarven.Puzzler;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
diff --git a/src/main/java/de/hysky/skyblocker/utils/render/Renderable.java b/src/main/java/de/hysky/skyblocker/utils/render/Renderable.java
new file mode 100644
index 00000000..b7743153
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/utils/render/Renderable.java
@@ -0,0 +1,7 @@
+package de.hysky.skyblocker.utils.render;
+
+import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
+
+public interface Renderable {
+ void render(WorldRenderContext context);
+}
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 eb30cf8d..3a1d364f 100644
--- a/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java
+++ b/src/main/java/de/hysky/skyblocker/utils/waypoint/Waypoint.java
@@ -19,15 +19,15 @@ public class Waypoint {
final boolean throughWalls;
private boolean shouldRender;
- protected Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents) {
+ public Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents) {
this(pos, typeSupplier, colorComponents, DEFAULT_HIGHLIGHT_ALPHA, DEFAULT_LINE_WIDTH);
}
- protected Waypoint(BlockPos pos, Type type, float[] colorComponents, float alpha) {
+ public Waypoint(BlockPos pos, Type type, float[] colorComponents, float alpha) {
this(pos, () -> type, colorComponents, alpha, DEFAULT_LINE_WIDTH);
}
- protected Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents, float alpha, float lineWidth) {
+ public Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents, float alpha, float lineWidth) {
this(pos, typeSupplier, colorComponents, alpha, lineWidth, true);
}
@@ -35,11 +35,11 @@ public class Waypoint {
this(pos, typeSupplier, colorComponents, DEFAULT_HIGHLIGHT_ALPHA, DEFAULT_LINE_WIDTH, throughWalls);
}
- protected Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents, float alpha, float lineWidth, boolean throughWalls) {
+ public Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents, float alpha, float lineWidth, boolean throughWalls) {
this(pos, typeSupplier, colorComponents, alpha, lineWidth, throughWalls, true);
}
- protected Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents, float alpha, float lineWidth, boolean throughWalls, boolean shouldRender) {
+ public Waypoint(BlockPos pos, Supplier<Type> typeSupplier, float[] colorComponents, float alpha, float lineWidth, boolean throughWalls, boolean shouldRender) {
this.pos = pos;
this.box = new Box(pos);
this.typeSupplier = typeSupplier;