diff options
Diffstat (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon')
3 files changed, 156 insertions, 28 deletions
diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java index c9603809..519b365d 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java @@ -1,5 +1,6 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.IntSortedSet; import net.minecraft.block.MapColor; import net.minecraft.item.map.MapIcon; @@ -7,6 +8,7 @@ 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; import org.jetbrains.annotations.Nullable; import org.joml.RoundingMode; @@ -19,6 +21,25 @@ public class DungeonMapUtils { public static final byte BLACK_COLOR = MapColor.BLACK.getRenderColorByte(MapColor.Brightness.LOWEST); public static final byte WHITE_COLOR = MapColor.WHITE.getRenderColorByte(MapColor.Brightness.HIGH); + public static byte getColor(MapState map, @Nullable Vector2ic pos) { + return pos == null ? -1 : getColor(map, pos.x(), pos.y()); + } + + public static byte getColor(MapState map, int x, int z) { + if (x < 0 || z < 0 || x >= 128 || z >= 128) { + return -1; + } + return map.colors[x + (z << 7)]; + } + + public static boolean isEntranceColor(MapState map, int x, int z) { + return getColor(map, x, z) == Room.Type.ENTRANCE.color; + } + + public static boolean isEntranceColor(MapState map, @Nullable Vector2ic pos) { + return getColor(map, pos) == Room.Type.ENTRANCE.color; + } + @Nullable private static Vector2i getMapPlayerPos(MapState map) { for (MapIcon icon : map.getIcons()) { @@ -32,15 +53,16 @@ public class DungeonMapUtils { @Nullable public static Vector2ic getMapEntrancePos(MapState map) { Vector2i mapPos = getMapPlayerPos(map); - if (!isEntranceColor(getColor(map, mapPos))) { + DungeonSecrets.LOGGER.info("[Skyblocker] Trying to get dungeon map entrance pos from map player pos at {}", mapPos); // TODO remove + if (!isEntranceColor(map, mapPos)) { return null; } // noinspection StatementWithEmptyBody, DataFlowIssue - while (isEntranceColor(getColor(map, mapPos.sub(1, 0)))) { + while (isEntranceColor(map, mapPos.sub(1, 0))) { } mapPos.add(1, 0); //noinspection StatementWithEmptyBody - while (isEntranceColor(getColor(map, mapPos.sub(0, 1)))) { + while (isEntranceColor(map, mapPos.sub(0, 1))) { } return mapPos.add(0, 1); } @@ -48,7 +70,7 @@ public class DungeonMapUtils { public static int getMapRoomSize(MapState map, Vector2ic mapEntrancePos) { int i = -1; //noinspection StatementWithEmptyBody - while (isEntranceColor(getColor(map, mapEntrancePos.x() + ++i, mapEntrancePos.y()))) { + while (isEntranceColor(map, mapEntrancePos.x() + ++i, mapEntrancePos.y())) { } return i; } @@ -91,7 +113,7 @@ public class DungeonMapUtils { @Nullable public static Vector2ic getPhysicalEntrancePos(MapState map, @NotNull Vec3d playerPos) { - if (isEntranceColor(getColor(map, getMapPlayerPos(map)))) { + if (isEntranceColor(map, getMapPlayerPos(map))) { return getPhysicalRoomPos(playerPos); } return null; @@ -112,6 +134,15 @@ public class DungeonMapUtils { return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8); } + /** + * @see #getPhysicalRoomPos(Vec3d) + */ + @NotNull + public static Vector2ic getPhysicalRoomPos(@NotNull Vec3i playerPos) { + Vector2i physicalPos = new Vector2i(playerPos.getX() + 8.5, playerPos.getZ() + 8.5, RoundingMode.TRUNCATE); + return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8); + } + public static Vector2ic[] getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomSize, Vector2ic physicalEntrancePos, Vector2ic... mapPositions) { for (int i = 0; i < mapPositions.length; i++) { mapPositions[i] = getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, mapPositions[i]); @@ -150,6 +181,19 @@ public class DungeonMapUtils { }; } + public static BlockPos relativeToActual(Vector2ic physicalCornerPos, Room.Direction direction, JsonObject posJson) { + return relativeToActual(physicalCornerPos, direction, new BlockPos(posJson.get("x").getAsInt(), posJson.get("y").getAsInt(), posJson.get("z").getAsInt())); + } + + public static BlockPos relativeToActual(Vector2ic physicalCornerPos, Room.Direction direction, BlockPos pos) { + return switch (direction) { + case NW -> new BlockPos(pos.getX() + physicalCornerPos.x(), pos.getY(), pos.getZ() + physicalCornerPos.y()); + case NE -> new BlockPos(-pos.getZ() + physicalCornerPos.x(), pos.getY(), pos.getX() + physicalCornerPos.y()); + case SW -> new BlockPos(pos.getZ() + physicalCornerPos.x(), pos.getY(), -pos.getX() + physicalCornerPos.y()); + case SE -> new BlockPos(-pos.getX() + physicalCornerPos.x(), pos.getY(), -pos.getZ() + physicalCornerPos.y()); + }; + } + public static Room.Type getRoomType(MapState map, Vector2ic mapPos) { return switch (getColor(map, mapPos)) { case 30 -> Room.Type.ENTRANCE; @@ -195,19 +239,4 @@ public class DungeonMapUtils { DungeonSecrets.LOGGER.debug("[Skyblocker] Found dungeon room segments: {}", Arrays.toString(segments.toArray())); return segments.toArray(Vector2ic[]::new); } - - public static byte getColor(MapState map, @Nullable Vector2ic pos) { - return pos == null ? -1 : getColor(map, pos.x(), pos.y()); - } - - public static byte getColor(MapState map, int x, int z) { - if (x < 0 || z < 0 || x >= 128 || z >= 128) { - return -1; - } - return map.colors[x + (z << 7)]; - } - - public static boolean isEntranceColor(byte color) { - return color == Room.Type.ENTRANCE.color; - } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index bcc26f78..03aa0ca7 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -6,6 +6,8 @@ import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.utils.Utils; +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.item.FilledMapItem; @@ -79,7 +81,7 @@ public class DungeonSecrets { */ private static Vector2ic mapEntrancePos; /** - * The width of a room on the map. + * The size of a room on the map. */ private static int mapRoomSize; /** @@ -93,6 +95,14 @@ public class DungeonSecrets { return roomsLoaded != null && roomsLoaded.isDone(); } + public static JsonObject getRoomsJson() { + return roomsJson; + } + + public static JsonObject getWaypointsJson() { + return waypointsJson; + } + /** * Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}. * Use {@link #isRoomsLoaded()} to check for completion of loading. @@ -103,6 +113,7 @@ public class DungeonSecrets { } CompletableFuture.runAsync(DungeonSecrets::load); SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonSecrets::update, 10); + WorldRenderEvents.AFTER_TRANSLUCENT.register(DungeonSecrets::render); } private static void load() { @@ -174,6 +185,7 @@ public class DungeonSecrets { } private static void update() { + long startTime = System.currentTimeMillis(); if (!SkyblockerConfig.get().locations.dungeons.secretWaypoints || !Utils.isInDungeons()) { return; } @@ -203,7 +215,7 @@ public class DungeonSecrets { return; } else { currentRoom = newRoom(Room.Type.ENTRANCE, physicalEntrancePos); - LOGGER.info("[Skyblocker] Started dungeon with map room width {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos); + LOGGER.info("[Skyblocker] Started dungeon with map room size {}, map entrance pos {}, player pos {}, and physical entrance pos {}", mapRoomSize, mapEntrancePos, client.player.getPos(), physicalEntrancePos); } } @@ -217,13 +229,16 @@ public class DungeonSecrets { if (room == null) { switch (type) { case ENTRANCE, PUZZLE, TRAP, MINIBOSS, FAIRY, BLOOD -> room = newRoom(type, physicalPos); - case ROOM -> room = newRoom(type, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, mapPos, mapRoomSize, type.color))); + case ROOM -> + room = newRoom(type, DungeonMapUtils.getPhysicalPosFromMap(mapEntrancePos, mapRoomSize, physicalEntrancePos, DungeonMapUtils.getRoomSegments(map, mapPos, mapRoomSize, type.color))); } } if (room != null && currentRoom != room) { currentRoom = room; } currentRoom.update(); + long endTime = System.currentTimeMillis(); + LOGGER.debug("[Skyblocker] Updated dungeon secrets in {} ms", endTime - startTime); // TODO change to debug } /** @@ -240,4 +255,11 @@ public class DungeonSecrets { } return newRoom; } + + private static void render(WorldRenderContext context) { + if (!SkyblockerConfig.get().locations.dungeons.secretWaypoints || !Utils.isInDungeons() || currentRoom.getName() == null) { + return; + } + currentRoom.render(context); + } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java index 6eabc027..d722d563 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java @@ -1,14 +1,19 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import it.unimi.dsi.fastutil.ints.IntRBTreeSet; import it.unimi.dsi.fastutil.ints.IntSortedSet; import it.unimi.dsi.fastutil.ints.IntSortedSets; +import me.xmrvizzy.skyblocker.utils.RenderHelper; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; 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.registry.Registries; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import org.apache.commons.lang3.tuple.MutableTriple; import org.apache.commons.lang3.tuple.Triple; import org.joml.Vector2i; @@ -29,8 +34,10 @@ public class Room { private String name; private Direction direction; private Vector2ic corner; + private final List<Secret> secrets = new ArrayList<>(); public Room(Type type, Vector2ic... physicalPositions) { + long startTime = System.currentTimeMillis(); this.type = type; segments = Set.of(physicalPositions); IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray())); @@ -41,13 +48,18 @@ public class Room { for (Direction direction : getPossibleDirections(segmentsX, segmentsY)) { possibleRooms.add(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), possibleDirectionRooms)); } - DungeonSecrets.LOGGER.info("Created {}", this); // TODO change to debug + long endTime = System.currentTimeMillis(); + DungeonSecrets.LOGGER.info("Created {} in {} ms", this, endTime - startTime); // TODO change to debug } public Type getType() { return type; } + public String getName() { + return name; + } + @Override public String toString() { return "Room{type=" + type + ", shape=" + shape + ", name='" + name + "', direction=" + direction + ", corner=" + corner + ", segments=" + Arrays.toString(segments.toArray()) + "}"; @@ -116,7 +128,8 @@ public class Room { findRoom = CompletableFuture.runAsync(() -> { long startTime = System.currentTimeMillis(); for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) { - if (checkedBlocks.add(pos) && checkBlock(world, pos)) { + // TODO Check if pos is outside of room or part of doorway + if (segments.contains(DungeonMapUtils.getPhysicalRoomPos(pos)) && notInDoorway(pos) && checkedBlocks.add(pos) && checkBlock(world, pos)) { reset(); break; } @@ -126,6 +139,15 @@ public class Room { }); } + public static boolean notInDoorway(BlockPos pos) { + if (pos.getY() < 66 || pos.getY() > 73) { + return true; + } + int x = MathHelper.floorMod(pos.getX() - 8, 32); + int z = MathHelper.floorMod(pos.getZ() - 8, 32); + return x >= 13 && x <= 17 && (z <= 2 || z >= 28) || z >= 13 && z <= 17 && (x <= 2 || x >= 28); + } + private boolean checkBlock(ClientWorld world, BlockPos pos) { byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString()); if (id == 0) { @@ -152,9 +174,8 @@ public class Room { } else if (matchingRoomsSize == 1) { for (Triple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) { if (directionRooms.getRight().size() == 1) { - name = directionRooms.getRight().get(0); - direction = directionRooms.getLeft(); - corner = directionRooms.getMiddle(); + roomMatched(directionRooms); + break; } } DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size()); // TODO change to debug @@ -169,6 +190,41 @@ public class Room { return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id; } + private void roomMatched(Triple<Direction, Vector2ic, List<String>> directionRooms) { + name = directionRooms.getRight().get(0); + direction = directionRooms.getLeft(); + corner = directionRooms.getMiddle(); + for (JsonElement waypointElement : DungeonSecrets.getWaypointsJson().get(name).getAsJsonArray()) { + JsonObject waypoint = waypointElement.getAsJsonObject(); + String name = waypoint.get("secretName").getAsString(); + int index = Integer.parseInt(Character.isDigit(name.charAt(1)) ? name.substring(0, 2) : name.substring(0, 1)); + secrets.add(new Secret(index, getCategory(waypoint), DungeonMapUtils.relativeToActual(corner, direction, waypoint), true)); + } + } + + private Category getCategory(JsonObject categoryJson) { + return switch (categoryJson.get("category").getAsString()) { + case "entrance" -> Category.ENTRANCE; + case "superboom" -> Category.SUPERBOOM; + case "chest" -> Category.CHEST; + case "item" -> Category.ITEM; + case "bat" -> Category.BAT; + case "wither" -> Category.WITHER; + case "lever" -> Category.LEVER; + case "fairysoul" -> Category.FAIRYSOUL; + case "stonk" -> Category.STONK; + default -> Category.DEFAULT; + }; + } + + protected void render(WorldRenderContext context) { + for (Secret secret : secrets) { + if (secret.missing()) { + RenderHelper.renderFilledThroughWallsWithBeaconBeam(context, secret.pos, secret.category.colorComponents, 0.5F); + } + } + } + /** * Resets fields after room matching completes, where either a room is found or none matched. * These fields are no longer needed and are discarded to save memory. @@ -217,4 +273,25 @@ public class Room { public enum Direction { NW, NE, SW, SE } + + public record Secret(int index, Category category, BlockPos pos, boolean missing) { + } + + public enum Category { + ENTRANCE(0, 255, 0), + SUPERBOOM(255, 0, 0), + CHEST(2, 213, 250), + ITEM(2, 64, 250), + BAT(142, 66, 0), + WITHER(30, 30, 30), + LEVER(250, 217, 2), + FAIRYSOUL(255, 85, 255), + STONK(146, 52, 235), + DEFAULT(190, 255, 252); + final float[] colorComponents; + + Category(float... colorComponents) { + this.colorComponents = colorComponents; + } + } } |