diff --git a/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/DepthFirstMaze.java b/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/DepthFirstMaze.java new file mode 100644 index 0000000..851c1f4 --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/DepthFirstMaze.java @@ -0,0 +1,89 @@ +package net.ludocrypt.limlib.api.world.maze.deprecated; + +import java.util.List; +import java.util.Random; +import java.util.Stack; + +import com.google.common.collect.Lists; + +@Deprecated +public class DepthFirstMaze extends MazeComponent { + + public Stack stack = new Stack(); + + public Random random; + + public DepthFirstMaze(int width, int height, Random random) { + super(width, height); + this.random = random; + } + + @Override + public void generateMaze() { + this.maze[0].visited(); + this.stack.push(new Vec2i(0, 0)); + while (visitedCells < this.width * this.height) { + List neighbours = Lists.newArrayList(); + + // North Neighbour + if (this.hasNorthNeighbor(this.stack.peek())) { + neighbours.add(0); + } + + // East Neighbour + if (this.hasEastNeighbor(this.stack.peek())) { + neighbours.add(1); + } + + // South Neighbour + if (this.hasSouthNeighbor(this.stack.peek())) { + neighbours.add(2); + } + + // West Neighbour + if (this.hasWestNeighbor(this.stack.peek())) { + neighbours.add(3); + } + + // Neighbour check + if (!neighbours.isEmpty()) { + int nextCellDir = neighbours.get(random.nextInt(neighbours.size())); + + switch (nextCellDir) { + case 0: // North + this.cellState(this.stack.peek().getX(), this.stack.peek().getY()).north(); + this.cellState(this.stack.peek().getX() + 1, this.stack.peek().getY()).south(); + this.cellState(this.stack.peek().getX() + 1, this.stack.peek().getY()).visited(); + this.stack.push(new Vec2i(this.stack.peek().getX() + 1, this.stack.peek().getY())); + break; + case 1: // East + this.cellState(this.stack.peek().getX(), this.stack.peek().getY()).east(); + this.cellState(this.stack.peek().getX(), this.stack.peek().getY() + 1).west(); + this.cellState(this.stack.peek().getX(), this.stack.peek().getY() + 1).visited(); + this.stack.push(new Vec2i(this.stack.peek().getX(), this.stack.peek().getY() + 1)); + break; + case 2: // South + this.cellState(this.stack.peek().getX(), this.stack.peek().getY()).south(); + this.cellState(this.stack.peek().getX() - 1, this.stack.peek().getY()).north(); + this.cellState(this.stack.peek().getX() - 1, this.stack.peek().getY()).visited(); + this.stack.push(new Vec2i(this.stack.peek().getX() - 1, this.stack.peek().getY())); + break; + case 3: // West + this.cellState(this.stack.peek().getX(), this.stack.peek().getY()).west(); + this.cellState(this.stack.peek().getX(), this.stack.peek().getY() - 1).east(); + this.cellState(this.stack.peek().getX(), this.stack.peek().getY() - 1).visited(); + this.stack.push(new Vec2i(this.stack.peek().getX(), this.stack.peek().getY() - 1)); + break; + } + + // Visit Cell + this.visitedCells++; + + } else { + // Backtrack + this.stack.pop(); + } + } + } + +} diff --git a/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/MazeComponent.java b/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/MazeComponent.java new file mode 100644 index 0000000..08a0b0e --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/MazeComponent.java @@ -0,0 +1,144 @@ +package net.ludocrypt.limlib.api.world.maze.deprecated; + +@Deprecated +public abstract class MazeComponent { + + public final int width; + public final int height; + + public final CellState[] maze; + + public int visitedCells = 0; + + public MazeComponent(int width, int height) { + this.width = width; + this.height = height; + this.maze = new CellState[width * height]; + for (int i = 0; i < width * height; i++) { + this.maze[i] = new CellState(); + } + this.visitedCells = 1; + } + + public abstract void generateMaze(); + + public CellState cellState(int x, int y) { + return this.maze[y * this.width + x]; + } + + public boolean hasNorthNeighbor(Vec2i vec) { + return (vec.getX() + 1 < this.height) && !(this.maze[((vec.getY()) * this.width + (vec.getX() + 1))].isVisited()); + } + + public boolean hasEastNeighbor(Vec2i vec) { + return (vec.getY() + 1 < this.width) && !(this.maze[((vec.getY() + 1) * this.width + (vec.getX()))].isVisited()); + } + + public boolean hasSouthNeighbor(Vec2i vec) { + return (vec.getX() > 0) && !(this.maze[((vec.getY()) * this.width + (vec.getX() - 1))].isVisited()); + } + + public boolean hasWestNeighbor(Vec2i vec) { + return (vec.getY() > 0) && !(this.maze[((vec.getY() - 1) * this.width + (vec.getX()))].isVisited()); + } + + public boolean hasNeighbors(Vec2i vec) { + return this.hasNorthNeighbor(vec) || this.hasEastNeighbor(vec) || this.hasSouthNeighbor(vec) || this.hasWestNeighbor(vec); + } + + public static class CellState { + + private boolean north = false; + private boolean east = false; + private boolean south = false; + private boolean west = false; + private boolean visited = false; + + public void north() { + this.north = true; + } + + public void east() { + this.east = true; + } + + public void south() { + this.south = true; + } + + public void west() { + this.west = true; + } + + public void visited() { + this.visited = true; + } + + public void setNorth(boolean north) { + this.north = north; + } + + public void setEast(boolean east) { + this.east = east; + } + + public void setSouth(boolean south) { + this.south = south; + } + + public void setWest(boolean west) { + this.west = west; + } + + public void setVisited(boolean visited) { + this.visited = visited; + } + + public boolean isNorth() { + return north; + } + + public boolean isEast() { + return east; + } + + public boolean isSouth() { + return south; + } + + public boolean isWest() { + return west; + } + + public boolean isVisited() { + return visited; + } + + } + + public static class Vec2i { + + private int x; + private int y; + + public Vec2i(int x, int y) { + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + @Override + public String toString() { + return "(" + this.x + ", " + this.y + ")"; + } + + } + +} diff --git a/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/MazeGenerator.java b/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/MazeGenerator.java new file mode 100644 index 0000000..3dbf9ba --- /dev/null +++ b/src/main/java/net/ludocrypt/limlib/api/world/maze/deprecated/MazeGenerator.java @@ -0,0 +1,92 @@ +package net.ludocrypt.limlib.api.world.maze.deprecated; + +import java.util.HashMap; +import java.util.Random; +import java.util.function.Function; + +import com.mojang.serialization.Codec; + +import net.ludocrypt.limlib.api.world.maze.deprecated.MazeComponent.CellState; +import net.ludocrypt.limlib.impl.LimlibRegistries; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.ChunkRegion; +import net.minecraft.world.chunk.Chunk; + +@Deprecated +public abstract class MazeGenerator { + + public static final Codec CODEC = LimlibRegistries.LIMINAL_MAZE_GENERATOR.getCodec().dispatchStable(MazeGenerator::getCodec, Function.identity()); + + private final HashMap mazes = new HashMap(30); + + public final int width; + public final int height; + public final int thickness; + public final boolean redundancy; + public final long seedModifier; + + public MazeGenerator(int width, int height, int thickness, boolean redundancy, long seedModifier) { + this.width = width; + this.height = height; + this.thickness = thickness; + this.redundancy = redundancy; + this.seedModifier = seedModifier; + } + + public void generateMaze(BlockPos pos, Chunk chunk, ChunkRegion region) { + if (thickness < 1) + throw new UnsupportedOperationException("Thickness can not be less than 1"); + + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + BlockPos inPos = pos.add(x, 0, y); + if (mod(inPos.getX(), thickness) == 0 && mod(inPos.getZ(), thickness) == 0) { + BlockPos mazePos = new BlockPos(inPos.getX() - mod(inPos.getX(), (width * thickness * 2)), 0, inPos.getZ() - mod(inPos.getZ(), (height * thickness * 2))); + + MazeComponent maze; + if (this.mazes.containsKey(mazePos)) { + maze = this.mazes.get(mazePos); + } else { + maze = this.newMaze(region, chunk, redundancy ? width + 4 : width, redundancy ? height + 4 : height, new Random(blockSeed(mazePos.getX(), mazePos.getZ(), seedModifier))); + this.mazes.put(mazePos, maze); + } + + BlockPos originPos = new BlockPos(inPos.getX() - mod(inPos.getX(), (2 * thickness)), 0, inPos.getZ() - mod(inPos.getZ(), (2 * thickness))); + + boolean isOpenCell = mod(inPos.getX(), (2 * thickness)) == 0 && mod(inPos.getZ(), (2 * thickness)) == 0; + boolean isWallCell = mod(inPos.getX(), (2 * thickness)) == thickness && mod(inPos.getZ(), (2 * thickness)) == thickness; + boolean isTopCell = mod(inPos.getX(), (2 * thickness)) == thickness && mod(inPos.getZ(), (2 * thickness)) == 0; + boolean isSideCell = mod(inPos.getX(), (2 * thickness)) == 0 && mod(inPos.getZ(), (2 * thickness)) == thickness; + + int mazeX = mod(originPos.getX(), (width * thickness * 2)) / (2 * thickness); + int mazeY = mod(originPos.getZ(), (height * thickness * 2)) / (2 * thickness); + + CellState originCell = maze.cellState(redundancy ? mazeX + 2 : mazeX, redundancy ? mazeY + 2 : mazeX); + + this.decorateCell(inPos, originPos, chunk, region, originCell, isOpenCell, isWallCell, isTopCell, isSideCell, thickness); + } + } + } + } + + public abstract MazeComponent newMaze(ChunkRegion region, Chunk chunk, int width, int height, Random random); + + public abstract void decorateCell(BlockPos pos, BlockPos origin, Chunk chunk, ChunkRegion region, CellState state, boolean isOpen, boolean isWall, boolean isTopCell, boolean isSideCell, int thickness); + + public abstract Codec getCodec(); + + protected int mod(int x, int n) { + int r = x % n; + if (r < 0) { + r += n; + } + return r; + } + + protected long blockSeed(long x, long y, long z) { + long l = (x * 3129871) ^ z * 116129781L ^ y; + l = l * l * 42317861L + l * 11L; + return l >> 16; + } + +} diff --git a/src/main/java/net/ludocrypt/limlib/impl/LimlibRegistries.java b/src/main/java/net/ludocrypt/limlib/impl/LimlibRegistries.java index 6eb0169..ebee261 100644 --- a/src/main/java/net/ludocrypt/limlib/impl/LimlibRegistries.java +++ b/src/main/java/net/ludocrypt/limlib/impl/LimlibRegistries.java @@ -8,10 +8,11 @@ import net.ludocrypt.limlib.api.render.LiminalBaseEffects; import net.ludocrypt.limlib.api.render.LiminalShaderApplier; import net.ludocrypt.limlib.api.render.LiminalSkyRenderer; +import net.ludocrypt.limlib.api.world.maze.deprecated.MazeGenerator; import net.minecraft.util.Identifier; import net.minecraft.util.registry.SimpleRegistry; -@SuppressWarnings("unchecked") +@SuppressWarnings({ "unchecked", "deprecation" }) public class LimlibRegistries { public static final SimpleRegistry LIMINAL_WORLD = FabricRegistryBuilder.createSimple(LiminalWorld.class, new Identifier("limlib", "limlib_world")).attribute(RegistryAttribute.SYNCED).buildAndRegister(); @@ -20,4 +21,6 @@ public class LimlibRegistries { public static final SimpleRegistry> LIMINAL_SHADER_APPLIER = (SimpleRegistry>) (Object) FabricRegistryBuilder.createSimple(Codec.class, new Identifier("limlib", "limlib_shader_applier")).attribute(RegistryAttribute.SYNCED).buildAndRegister(); public static final SimpleRegistry> LIMINAL_BASE_EFFECTS = (SimpleRegistry>) (Object) FabricRegistryBuilder.createSimple(Codec.class, new Identifier("limlib", "limlib_base_effects")).attribute(RegistryAttribute.SYNCED).buildAndRegister(); + public static final SimpleRegistry> LIMINAL_MAZE_GENERATOR = (SimpleRegistry>) (Object) FabricRegistryBuilder.createSimple(Codec.class, new Identifier("limlib", "limlib_maze_generator")).attribute(RegistryAttribute.SYNCED).buildAndRegister(); + }