aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker/skyblock/dungeon
diff options
context:
space:
mode:
authormsg-programs <msgdoesstuff@gmail.com>2023-10-31 21:03:42 +0100
committermsg-programs <msgdoesstuff@gmail.com>2023-10-31 21:03:42 +0100
commitd560c4611e603fa9e72ff6842bc14518d7bdbd63 (patch)
tree36eb1e24443bc9d9fbba51e14f12e5aacfac935c /src/main/java/de/hysky/skyblocker/skyblock/dungeon
parent1df4ef827d8a2e2fcc3767c1f5bf961f16b7fa19 (diff)
parent5bb91104d3275283d7479f0b35c1b18be470d632 (diff)
downloadSkyblocker-d560c4611e603fa9e72ff6842bc14518d7bdbd63.tar.gz
Skyblocker-d560c4611e603fa9e72ff6842bc14518d7bdbd63.tar.bz2
Skyblocker-d560c4611e603fa9e72ff6842bc14518d7bdbd63.zip
Merge branch 'master' of https://github.com/SkyblockerMod/Skyblocker into cleanup-2
# Conflicts: # src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java # src/main/java/de/hysky/skyblocker/utils/Http.java # src/main/java/de/hysky/skyblocker/utils/render/title/TitleContainerConfigScreen.java Pull changes from upstream master
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock/dungeon')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java8
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java9
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java238
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java127
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretWaypoint.java144
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/SecretsTracker.java174
10 files changed, 614 insertions, 96 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java
index 6369291b..689788e1 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/CreeperBeams.java
@@ -237,12 +237,12 @@ public class CreeperBeams {
// render either in a color if not created or faintly green if created
public void render(WorldRenderContext wrc, float[] color) {
if (toDo) {
- RenderHelper.renderOutline(wrc, outlineOne, color, 3);
- RenderHelper.renderOutline(wrc, outlineTwo, color, 3);
+ RenderHelper.renderOutline(wrc, outlineOne, color, 3, false);
+ RenderHelper.renderOutline(wrc, outlineTwo, color, 3, false);
RenderHelper.renderLinesFromPoints(wrc, line, color, 1, 2);
} else {
- RenderHelper.renderOutline(wrc, outlineOne, LIME_COLOR_COMPONENTS, 1);
- RenderHelper.renderOutline(wrc, outlineTwo, LIME_COLOR_COMPONENTS, 1);
+ RenderHelper.renderOutline(wrc, outlineOne, LIME_COLOR_COMPONENTS, 1, false);
+ RenderHelper.renderOutline(wrc, outlineTwo, LIME_COLOR_COMPONENTS, 1, false);
RenderHelper.renderLinesFromPoints(wrc, line, LIME_COLOR_COMPONENTS, 0.75f, 1);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java
index cfb16b4d..f49a2f2e 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonBlaze.java
@@ -128,11 +128,11 @@ public class DungeonBlaze {
*/
private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) {
Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(wrc, blazeBox, GREEN_COLOR_COMPONENTS, 5f);
+ RenderHelper.renderOutline(wrc, blazeBox, GREEN_COLOR_COMPONENTS, 5f, false);
if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) {
Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0);
- RenderHelper.renderOutline(wrc, nextBlazeBox, WHITE_COLOR_COMPONENTS, 5f);
+ RenderHelper.renderOutline(wrc, nextBlazeBox, WHITE_COLOR_COMPONENTS, 5f, false);
Vec3d blazeCenter = blazeBox.getCenter();
Vec3d nextBlazeCenter = nextBlazeBox.getCenter();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java
index 4e6a5240..1e889af3 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonChestProfit.java
@@ -4,7 +4,8 @@ import com.google.gson.JsonObject;
import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.mixin.accessor.ScreenAccessor;
-import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
import de.hysky.skyblocker.utils.Utils;
import it.unimi.dsi.fastutil.ints.IntBooleanPair;
import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
@@ -54,7 +55,7 @@ public class DungeonChestProfit {
if (!stack.isEmpty()) {
String name = stack.getName().getString();
- String id = PriceInfoTooltip.getInternalNameFromNBT(stack, false);
+ String id = ItemTooltip.getInternalNameFromNBT(stack, false);
//Regular item price
if (id != null) {
@@ -128,8 +129,8 @@ public class DungeonChestProfit {
* was based on complete data.
*/
private static IntBooleanPair getItemPrice(String id) {
- JsonObject bazaarPrices = PriceInfoTooltip.getBazaarPrices();
- JsonObject lbinPrices = PriceInfoTooltip.getLBINPrices();
+ JsonObject bazaarPrices = TooltipInfoType.BAZAAR.getData();
+ JsonObject lbinPrices = TooltipInfoType.LOWEST_BINS.getData();
if (bazaarPrices == null || lbinPrices == null) return IntBooleanPair.of(0, false);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
index 69018cd4..c198238b 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/DungeonMapConfigScreen.java
@@ -57,6 +57,7 @@ public class DungeonMapConfigScreen extends Screen {
SkyblockerConfigManager.get().locations.dungeons.mapX = hudX;
SkyblockerConfigManager.get().locations.dungeons.mapY = hudY;
SkyblockerConfigManager.save();
+
this.client.setScreen(parent);
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java
index 2d56c8a0..7f249e7d 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/TicTacToe.java
@@ -127,7 +127,7 @@ public class TicTacToe {
private static void solutionRenderer(WorldRenderContext context) {
try {
if (SkyblockerConfigManager.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) {
- RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5);
+ RenderHelper.renderOutline(context, nextBestMoveToMake, RED_COLOR_COMPONENTS, 5, false);
}
} catch (Exception e) {
LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java
index 259cc3f3..73d4a452 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
@@ -7,7 +7,6 @@ import net.minecraft.block.MapColor;
import net.minecraft.item.map.MapIcon;
import net.minecraft.item.map.MapState;
import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.Vec3i;
import org.jetbrains.annotations.NotNull;
@@ -173,7 +172,7 @@ public class DungeonMapUtils {
@NotNull
public static Vector2ic getPhysicalRoomPos(double x, double z) {
Vector2i physicalPos = new Vector2i(x + 8.5, z + 8.5, RoundingMode.TRUNCATE);
- return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8);
+ return physicalPos.sub(Math.floorMod(physicalPos.x(), 32), Math.floorMod(physicalPos.y(), 32)).sub(8, 8);
}
public static Vector2ic[] getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic... mapPositions) {
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
index c2358689..eda08cf6 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java
@@ -1,5 +1,7 @@
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;
@@ -7,15 +9,19 @@ import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
-import it.unimi.dsi.fastutil.objects.Object2ByteMap;
-import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap;
-import it.unimi.dsi.fastutil.objects.ObjectIntPair;
+import com.mojang.brigadier.context.CommandContext;
+import com.mojang.serialization.JsonOps;
import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Constants;
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.Object2ByteOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectIntPair;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
@@ -23,6 +29,9 @@ import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
+import net.minecraft.command.argument.BlockPosArgumentType;
+import net.minecraft.command.argument.PosArgument;
+import net.minecraft.command.argument.TextArgumentType;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.mob.AmbientEntity;
@@ -32,11 +41,15 @@ import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.map.MapState;
import net.minecraft.resource.Resource;
+import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.text.Text;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Identifier;
import net.minecraft.util.hit.BlockHitResult;
+import net.minecraft.util.hit.HitResult;
+import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.math.Vec3i;
import net.minecraft.world.World;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@@ -46,10 +59,14 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
import java.util.zip.InflaterInputStream;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
@@ -58,6 +75,7 @@ import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.lit
public class DungeonSecrets {
protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class);
private static final String DUNGEONS_PATH = "dungeons";
+ private static final Path CUSTOM_WAYPOINTS_DIR = SkyblockerMod.CONFIG_DIR.resolve("custom_secret_waypoints.json");
/**
* Maps the block identifier string to a custom numeric block id used in dungeon rooms data.
*
@@ -97,6 +115,10 @@ public class DungeonSecrets {
private static final Map<Vector2ic, Room> rooms = new HashMap<>();
private static final Map<String, JsonElement> roomsJson = new HashMap<>();
private static final Map<String, JsonElement> waypointsJson = new HashMap<>();
+ /**
+ * The map of dungeon room names to custom waypoints relative to the room.
+ */
+ private static final Table<String, BlockPos, SecretWaypoint> customWaypoints = HashBasedTable.create();
@Nullable
private static CompletableFuture<Void> roomsLoaded;
/**
@@ -119,6 +141,10 @@ public class DungeonSecrets {
return roomsLoaded != null && roomsLoaded.isDone();
}
+ public static Stream<Room> getRoomsStream() {
+ return rooms.values().stream();
+ }
+
@SuppressWarnings("unused")
public static JsonObject getRoomMetadata(String room) {
return roomsJson.get(room).getAsJsonObject();
@@ -129,6 +155,38 @@ public class DungeonSecrets {
}
/**
+ * @see #customWaypoints
+ */
+ public static Map<BlockPos, SecretWaypoint> getCustomWaypoints(String room) {
+ return customWaypoints.row(room);
+ }
+
+ /**
+ * @see #customWaypoints
+ */
+ @SuppressWarnings("UnusedReturnValue")
+ public static SecretWaypoint addCustomWaypoint(String room, SecretWaypoint waypoint) {
+ return customWaypoints.put(room, waypoint.pos, waypoint);
+ }
+
+ /**
+ * @see #customWaypoints
+ */
+ public static void addCustomWaypoints(String room, Collection<SecretWaypoint> waypoints) {
+ for (SecretWaypoint waypoint : waypoints) {
+ addCustomWaypoint(room, waypoint);
+ }
+ }
+
+ /**
+ * @see #customWaypoints
+ */
+ @Nullable
+ public static SecretWaypoint removeCustomWaypoint(String room, BlockPos pos) {
+ return customWaypoints.remove(room, pos);
+ }
+
+ /**
* Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}.
* Use {@link #isRoomsLoaded()} to check for completion of loading.
*/
@@ -138,9 +196,10 @@ public class DungeonSecrets {
}
// Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set
CompletableFuture.runAsync(DungeonSecrets::load, MinecraftClient.getInstance()).exceptionally(e -> {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
+ 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);
@@ -148,7 +207,14 @@ public class DungeonSecrets {
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("markAsMissing").then(markSecretsCommand(false)))
+ .then(literal("getRelativePos").executes(DungeonSecrets::getRelativePos))
+ .then(literal("getRelativeTargetPos").executes(DungeonSecrets::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)))
+ ))));
ClientPlayConnectionEvents.JOIN.register(((handler, sender, client) -> reset()));
}
@@ -158,7 +224,7 @@ public class DungeonSecrets {
for (Map.Entry<Identifier, Resource> resourceEntry : MinecraftClient.getInstance().getResourceManager().findResources(DUNGEONS_PATH, id -> id.getPath().endsWith(".skeleton")).entrySet()) {
String[] path = resourceEntry.getKey().getPath().split("/");
if (path.length != 4) {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets, invalid resource identifier {}", resourceEntry.getKey());
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to load dungeon secrets, invalid resource identifier {}", resourceEntry.getKey());
break;
}
String dungeon = path[1];
@@ -171,9 +237,9 @@ public class DungeonSecrets {
synchronized (roomsMap) {
roomsMap.put(room, rooms);
}
- LOGGER.debug("[Skyblocker] Loaded dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room);
+ LOGGER.debug("[Skyblocker Dungeon Secrets] Loaded dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room);
}).exceptionally(e -> {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room, e);
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to load dungeon secrets dungeon {} room shape {} room {}", dungeon, roomShape, room, e);
return null;
}));
}
@@ -181,16 +247,39 @@ public class DungeonSecrets {
try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) {
loadJson(roomsReader, roomsJson);
loadJson(waypointsReader, waypointsJson);
- LOGGER.debug("[Skyblocker] Loaded dungeon secrets json");
+ LOGGER.debug("[Skyblocker Dungeon Secrets] Loaded dungeon secret waypoints json");
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to load dungeon secret waypoints json", e);
+ }
+ }));
+ dungeonFutures.add(CompletableFuture.runAsync(() -> {
+ try (BufferedReader customWaypointsReader = Files.newBufferedReader(CUSTOM_WAYPOINTS_DIR)) {
+ SkyblockerMod.GSON.fromJson(customWaypointsReader, JsonObject.class).asMap().forEach((room, waypointsJson) ->
+ addCustomWaypoints(room, SecretWaypoint.LIST_CODEC.parse(JsonOps.INSTANCE, waypointsJson).resultOrPartial(LOGGER::error).orElseThrow())
+ );
+ LOGGER.debug("[Skyblocker Dungeon Secrets] Loaded custom dungeon secret waypoints");
} catch (Exception e) {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets json", e);
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to load custom dungeon secret waypoints", e);
}
}));
- roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker] Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total in {} ms", ROOMS_DATA.size(), ROOMS_DATA.values().stream().mapToInt(Map::size).sum(), ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).mapToInt(Map::size).sum(), System.currentTimeMillis() - startTime)).exceptionally(e -> {
- LOGGER.error("[Skyblocker] Failed to load dungeon secrets", e);
+ roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker Dungeon Secrets] Loaded dungeon secrets for {} dungeon(s), {} room shapes, {} rooms, and {} custom secret waypoints total in {} ms", ROOMS_DATA.size(), ROOMS_DATA.values().stream().mapToInt(Map::size).sum(), ROOMS_DATA.values().stream().map(Map::values).flatMap(Collection::stream).mapToInt(Map::size).sum(), customWaypoints.size(), System.currentTimeMillis() - startTime)).exceptionally(e -> {
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to load dungeon secrets", e);
return null;
});
- LOGGER.info("[Skyblocker] Started loading dungeon secrets in (blocked main thread for) {} ms", System.currentTimeMillis() - startTime);
+ LOGGER.info("[Skyblocker Dungeon Secrets] Started loading dungeon secrets in (blocked main thread for) {} ms", System.currentTimeMillis() - startTime);
+ }
+
+ private static void saveCustomWaypoints(MinecraftClient client) {
+ try (BufferedWriter writer = Files.newBufferedWriter(CUSTOM_WAYPOINTS_DIR)) {
+ JsonObject customWaypointsJson = new JsonObject();
+ customWaypoints.rowMap().forEach((room, waypoints) ->
+ customWaypointsJson.add(room, SecretWaypoint.LIST_CODEC.encodeStart(JsonOps.INSTANCE, new ArrayList<>(waypoints.values())).resultOrPartial(LOGGER::error).orElseThrow())
+ );
+ SkyblockerMod.GSON.toJson(customWaypointsJson, writer);
+ LOGGER.info("[Skyblocker Dungeon Secrets] Saved custom dungeon secret waypoints");
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to save custom dungeon secret waypoints", e);
+ }
}
private static int[] readRoom(Resource resource) throws RuntimeException {
@@ -203,25 +292,110 @@ public class DungeonSecrets {
/**
* Loads the json from the given {@link BufferedReader} into the given {@link Map}.
+ *
* @param reader the reader to read the json from
- * @param map the map to load into
+ * @param map the map to load into
*/
private static void loadJson(BufferedReader reader, Map<String, JsonElement> map) {
SkyblockerMod.GSON.fromJson(reader, JsonObject.class).asMap().forEach((room, jsonElement) -> map.put(room.toLowerCase().replaceAll(" ", "-"), jsonElement));
}
private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, Integer>> markSecretsCommand(boolean found) {
- return argument("secret", IntegerArgumentType.integer()).executes(context -> {
- int secretIndex = IntegerArgumentType.getInteger(context, "secret");
+ return argument("secretIndex", IntegerArgumentType.integer()).executes(context -> {
+ int secretIndex = IntegerArgumentType.getInteger(context, "secretIndex");
if (markSecrets(secretIndex, found)) {
- context.getSource().sendFeedback(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFound" : "skyblocker.dungeons.secrets.markSecretMissing", secretIndex));
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFound" : "skyblocker.dungeons.secrets.markSecretMissing", secretIndex)));
} else {
- context.getSource().sendError(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFoundUnable" : "skyblocker.dungeons.secrets.markSecretMissingUnable", secretIndex));
+ context.getSource().sendError(Constants.PREFIX.get().append(Text.translatable(found ? "skyblocker.dungeons.secrets.markSecretFoundUnable" : "skyblocker.dungeons.secrets.markSecretMissingUnable", secretIndex)));
}
return Command.SINGLE_SUCCESS;
});
}
+ private static int getRelativePos(CommandContext<FabricClientCommandSource> context) {
+ return getRelativePos(context.getSource(), context.getSource().getPlayer().getBlockPos());
+ }
+
+ private static int getRelativeTargetPos(CommandContext<FabricClientCommandSource> context) {
+ if (MinecraftClient.getInstance().crosshairTarget instanceof BlockHitResult blockHitResult && blockHitResult.getType() == HitResult.Type.BLOCK) {
+ return getRelativePos(context.getSource(), blockHitResult.getBlockPos());
+ } else {
+ context.getSource().sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.noTarget")));
+ }
+ return Command.SINGLE_SUCCESS;
+ }
+
+ private static int getRelativePos(FabricClientCommandSource source, BlockPos pos) {
+ 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())));
+ } 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) {
+ return argument("pos", BlockPosArgumentType.blockPos())
+ .then(argument("secretIndex", IntegerArgumentType.integer())
+ .then(argument("category", SecretWaypoint.Category.CategoryArgumentType.category())
+ .then(argument("name", TextArgumentType.text()).executes(context -> {
+ // TODO Less hacky way with custom ClientBlockPosArgumentType
+ BlockPos pos = context.getArgument("pos", PosArgument.class).toAbsoluteBlockPos(new ServerCommandSource(null, context.getSource().getPosition(), context.getSource().getRotation(), null, 0, null, null, null, null));
+ return relative ? addCustomWaypointRelative(context, pos) : addCustomWaypoint(context, pos);
+ }))
+ )
+ );
+ }
+
+ private static int addCustomWaypoint(CommandContext<FabricClientCommandSource> context, BlockPos pos) {
+ Room room = getRoomAtPhysical(pos);
+ if (isRoomMatched(room)) {
+ room.addCustomWaypoint(context, room.actualToRelative(pos));
+ } else {
+ context.getSource().sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.notMatched")));
+ }
+ return Command.SINGLE_SUCCESS;
+ }
+
+ private static int addCustomWaypointRelative(CommandContext<FabricClientCommandSource> context, BlockPos pos) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.addCustomWaypoint(context, pos);
+ } else {
+ context.getSource().sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.notMatched")));
+ }
+ return Command.SINGLE_SUCCESS;
+ }
+
+ private static ArgumentBuilder<FabricClientCommandSource, RequiredArgumentBuilder<FabricClientCommandSource, PosArgument>> removeCustomWaypointCommand(boolean relative) {
+ return argument("pos", BlockPosArgumentType.blockPos())
+ .executes(context -> {
+ // TODO Less hacky way with custom ClientBlockPosArgumentType
+ BlockPos pos = context.getArgument("pos", PosArgument.class).toAbsoluteBlockPos(new ServerCommandSource(null, context.getSource().getPosition(), context.getSource().getRotation(), null, 0, null, null, null, null));
+ return relative ? removeCustomWaypointRelative(context, pos) : removeCustomWaypoint(context, pos);
+ });
+ }
+
+ private static int removeCustomWaypoint(CommandContext<FabricClientCommandSource> context, BlockPos pos) {
+ Room room = getRoomAtPhysical(pos);
+ if (isRoomMatched(room)) {
+ room.removeCustomWaypoint(context, room.actualToRelative(pos));
+ } else {
+ context.getSource().sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.notMatched")));
+ }
+ return Command.SINGLE_SUCCESS;
+ }
+
+ private static int removeCustomWaypointRelative(CommandContext<FabricClientCommandSource> context, BlockPos pos) {
+ if (isCurrentRoomMatched()) {
+ currentRoom.removeCustomWaypoint(context, pos);
+ } else {
+ context.getSource().sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.notMatched")));
+ }
+ return Command.SINGLE_SUCCESS;
+ }
+
/**
* Updates the dungeon. The general idea is similar to the Dungeon Rooms Mod.
* <p></p>
@@ -282,7 +456,7 @@ public class DungeonSecrets {
}
mapEntrancePos = mapEntrancePosAndSize.left();
mapRoomSize = mapEntrancePosAndSize.rightInt();
- LOGGER.info("[Skyblocker] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos);
+ LOGGER.info("[Skyblocker Dungeon Secrets] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos);
}
Vector2ic physicalPos = DungeonMapUtils.getPhysicalRoomPos(client.player.getPos());
@@ -320,7 +494,7 @@ public class DungeonSecrets {
}
return newRoom;
} catch (IllegalArgumentException e) {
- LOGGER.error("[Skyblocker] Failed to create room", e);
+ LOGGER.error("[Skyblocker Dungeon Secrets] Failed to create room", e);
}
return null;
}
@@ -339,9 +513,16 @@ public class DungeonSecrets {
* Used to detect when all secrets in a room are found.
*/
private static void onChatMessage(Text text, boolean overlay) {
+ String message = text.getString();
+
if (overlay && isCurrentRoomMatched()) {
- currentRoom.onChatMessage(text.getString());
+ currentRoom.onChatMessage(message);
}
+
+ if (message.equals("[BOSS] Bonzo: Gratz for making it this far, but I'm basically unbeatable.") || message.equals("[BOSS] Scarf: This is where the journey ends for you, Adventurers.")
+ || message.equals("[BOSS] The Professor: I was burdened with terrible news recently...") || message.equals("[BOSS] Thorn: Welcome Adventurers! I am Thorn, the Spirit! And host of the Vegan Trials!")
+ || message.equals("[BOSS] Livid: Welcome, you've arrived right on time. I am Livid, the Master of Shadows.") || message.equals("[BOSS] Sadan: So you made it all the way here... Now you wish to defy me? Sadan?!")
+ || message.equals("[BOSS] Maxor: WELL! WELL! WELL! LOOK WHO'S HERE!")) reset();
}
/**
@@ -410,6 +591,19 @@ public class DungeonSecrets {
}
/**
+ * Gets the room at the given physical position.
+ *
+ * @param pos the physical position
+ * @return the room at the given physical position, or null if there is no room at the given physical position
+ * @see #rooms
+ * @see DungeonMapUtils#getPhysicalRoomPos(Vec3i)
+ */
+ @Nullable
+ private static Room getRoomAtPhysical(Vec3i pos) {
+ return rooms.get(DungeonMapUtils.getPhysicalRoomPos(pos));
+ }
+
+ /**
* Calls {@link #isRoomMatched(Room)} on {@link #currentRoom}.
*
* @return {@code true} if {@link #currentRoom} is not null and {@link #isRoomMatched(Room)}
@@ -439,7 +633,7 @@ public class DungeonSecrets {
}
/**
- * Resets fields when leaving a dungeon.
+ * Resets fields when leaving a dungeon or entering boss.
*/
private static void reset() {
mapEntrancePos = null;
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 dd7dc91e..ecfcf496 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
@@ -1,14 +1,17 @@
package de.hysky.skyblocker.skyblock.dungeon.secrets;
import com.google.common.collect.HashBasedTable;
-import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import com.mojang.brigadier.arguments.IntegerArgumentType;
+import com.mojang.brigadier.context.CommandContext;
+import de.hysky.skyblocker.utils.Constants;
+import de.hysky.skyblocker.utils.scheduler.Scheduler;
import it.unimi.dsi.fastutil.ints.IntRBTreeSet;
import it.unimi.dsi.fastutil.ints.IntSortedSet;
import it.unimi.dsi.fastutil.ints.IntSortedSets;
-import de.hysky.skyblocker.utils.scheduler.Scheduler;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.fabricmc.fabric.api.util.TriState;
import net.minecraft.block.BlockState;
@@ -21,13 +24,14 @@ 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.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.MathHelper;
import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.MutableTriple;
import org.apache.commons.lang3.tuple.Triple;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.joml.Vector2i;
import org.joml.Vector2ic;
@@ -72,6 +76,9 @@ public class Room {
*/
private TriState matched = TriState.DEFAULT;
private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints;
+ private String name;
+ private Direction direction;
+ private Vector2ic physicalCornerPos;
public Room(@NotNull Type type, @NotNull Vector2ic... physicalPositions) {
this.type = type;
@@ -92,6 +99,13 @@ public class Room {
return matched == TriState.TRUE;
}
+ /**
+ * Not null if {@link #isMatched()}.
+ */
+ public String getName() {
+ return name;
+ }
+
@Override
public String toString() {
return "Room{type=" + type + ", shape=" + shape + ", matched=" + matched + ", segments=" + Arrays.toString(segments.toArray()) + "}";
@@ -145,6 +159,79 @@ public class Room {
}
/**
+ * @see #addCustomWaypoint(int, SecretWaypoint.Category, Text, BlockPos)
+ */
+ protected void addCustomWaypoint(CommandContext<FabricClientCommandSource> context, BlockPos pos) {
+ int secretIndex = IntegerArgumentType.getInteger(context, "secretIndex");
+ SecretWaypoint.Category category = SecretWaypoint.Category.CategoryArgumentType.getCategory(context, "category");
+ Text waypointName = context.getArgument("name", Text.class);
+ addCustomWaypoint(secretIndex, category, waypointName, pos);
+ context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.dungeons.secrets.customWaypointAdded", pos.getX(), pos.getY(), pos.getZ(), name, secretIndex, category, waypointName)));
+ }
+
+ /**
+ * Adds a custom waypoint relative to this room to {@link DungeonSecrets#customWaypoints} and all existing instances of this r