diff --git a/src/main/java/StevenDimDoors/experimental/DirectedGraph.java b/src/main/java/StevenDimDoors/experimental/DirectedGraph.java new file mode 100644 index 00000000..cd56c751 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/DirectedGraph.java @@ -0,0 +1,225 @@ +package StevenDimDoors.experimental; + +/** + * Provides a complete implementation of a directed graph. + * @author SenseiKiwi + * + * @param The type of data to store in the graph's nodes + * @param The type of data to store in the graph's edges + */ +public class DirectedGraph +{ + private static class GraphNode implements IGraphNode + { + private LinkedList> inbound; + private LinkedList> outbound; + private ILinkedListNode> graphEntry; + private P data; + + public GraphNode(P data, LinkedList> graphList) + { + this.data = data; + this.inbound = new LinkedList>(); + this.outbound = new LinkedList>(); + this.graphEntry = graphList.addLast(this); + } + + public int indegree() + { + return inbound.size(); + } + + public int outdegree() + { + return outbound.size(); + } + + public Iterable> inbound() + { + return inbound; + } + + public Iterable> outbound() + { + return outbound; + } + + public P data() + { + return data; + } + + public void remove() + { + graphEntry.remove(); + graphEntry = null; + + for (Edge edge : inbound) + edge.remove(); + + for (Edge edge : outbound) + edge.remove(); + + inbound = null; + outbound = null; + data = null; + } + } + + private static class Edge implements IEdge + { + private GraphNode head; + private GraphNode tail; + private ILinkedListNode> headEntry; + private ILinkedListNode> tailEntry; + private ILinkedListNode> graphEntry; + private Q data; + + public Edge(GraphNode head, GraphNode tail, Q data, LinkedList> graphList) + { + this.head = head; + this.tail = tail; + this.data = data; + this.graphEntry = graphList.addLast(this); + this.headEntry = head.outbound.addLast(this); + this.tailEntry = tail.inbound.addLast(this); + } + + public IGraphNode head() + { + return head; + } + + public IGraphNode tail() + { + return tail; + } + + public Q data() + { + return data; + } + + public void remove() + { + headEntry.remove(); + tailEntry.remove(); + graphEntry.remove(); + headEntry = null; + tailEntry = null; + graphEntry = null; + head = null; + tail = null; + data = null; + } + } + + private LinkedList> nodes; + private LinkedList> edges; + + public DirectedGraph() + { + nodes = new LinkedList>(); + edges = new LinkedList>(); + } + + public int nodeCount() + { + return nodes.size(); + } + + public int edgeCount() + { + return edges.size(); + } + + public boolean isEmpty() + { + return nodes.isEmpty(); + } + + public Iterable> nodes() + { + return nodes; + } + + public Iterable> edges() + { + return edges; + } + + private GraphNode checkNode(IGraphNode node) + { + GraphNode innerNode = (GraphNode) node; + + // Check that this node actually belongs to this graph instance. + // Accepting foreign nodes could corrupt the graph's internal state. + if (innerNode.graphEntry.owner() != nodes) + { + throw new IllegalArgumentException("The specified node does not belong to this graph."); + } + return innerNode; + } + + private Edge checkEdge(IEdge edge) + { + Edge innerEdge = (Edge) edge; + + // Check that this node actually belongs to this graph instance. + // Accepting foreign nodes could corrupt the graph's internal state. + if (innerEdge.graphEntry.owner() != edges) + { + throw new IllegalArgumentException("The specified edge does not belong to this graph."); + } + return innerEdge; + } + + public IGraphNode addNode(U data) + { + return new GraphNode(data, nodes); + } + + public IEdge addEdge(IGraphNode head, IGraphNode tail, V data) + { + GraphNode innerHead = checkNode(head); + GraphNode innerTail = checkNode(tail); + return new Edge(innerHead, innerTail, data, edges); + } + + public U removeNode(IGraphNode node) + { + GraphNode innerNode = checkNode(node); + U data = innerNode.data(); + innerNode.remove(); + return data; + } + + public V removeEdge(IEdge edge) + { + Edge innerEdge = checkEdge(edge); + V data = innerEdge.data(); + innerEdge.remove(); + return data; + } + + public IEdge findEdge(IGraphNode head, IGraphNode tail) + { + for (IEdge edge : head.outbound()) + { + if (edge.tail() == tail) + return edge; + } + return null; + } + + public void clear() + { + // Remove each node individually to guarantee that all external + // references are invalidated. That'll prevent memory leaks and + // keep external code from using removed nodes or edges. + for (GraphNode node : nodes) + { + node.remove(); + } + } +} diff --git a/src/main/java/StevenDimDoors/experimental/DoorwayData.java b/src/main/java/StevenDimDoors/experimental/DoorwayData.java index a1a09f1c..ec1c5cc8 100644 --- a/src/main/java/StevenDimDoors/experimental/DoorwayData.java +++ b/src/main/java/StevenDimDoors/experimental/DoorwayData.java @@ -4,35 +4,21 @@ import StevenDimDoors.mod_pocketDim.Point3D; public class DoorwayData { - public final char X_AXIS = 'X'; - public final char Y_AXIS = 'Y'; - public final char Z_AXIS = 'Z'; + public static final char X_AXIS = 'X'; + public static final char Y_AXIS = 'Y'; + public static final char Z_AXIS = 'Z'; - private RoomNode head; - private RoomNode tail; private Point3D minCorner; private Point3D maxCorner; private char axis; - public DoorwayData(RoomNode head, RoomNode tail, Point3D minCorner, Point3D maxCorner, char axis) + public DoorwayData(Point3D minCorner, Point3D maxCorner, char axis) { - this.head = head; - this.tail = tail; this.minCorner = minCorner; this.maxCorner = maxCorner; this.axis = axis; } - public RoomNode head() - { - return head; - } - - public RoomNode tail() - { - return tail; - } - public Point3D minCorner() { return minCorner; diff --git a/src/main/java/StevenDimDoors/experimental/IEdge.java b/src/main/java/StevenDimDoors/experimental/IEdge.java new file mode 100644 index 00000000..1d268116 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/IEdge.java @@ -0,0 +1,8 @@ +package StevenDimDoors.experimental; + +public interface IEdge +{ + public IGraphNode head(); + public IGraphNode tail(); + public V data(); +} diff --git a/src/main/java/StevenDimDoors/experimental/IGraphNode.java b/src/main/java/StevenDimDoors/experimental/IGraphNode.java new file mode 100644 index 00000000..65fd2fbf --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/IGraphNode.java @@ -0,0 +1,10 @@ +package StevenDimDoors.experimental; + +public interface IGraphNode +{ + public Iterable> inbound(); + public Iterable> outbound(); + public int indegree(); + public int outdegree(); + public U data(); +} diff --git a/src/main/java/StevenDimDoors/experimental/ILinkedListNode.java b/src/main/java/StevenDimDoors/experimental/ILinkedListNode.java new file mode 100644 index 00000000..a7a54adc --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/ILinkedListNode.java @@ -0,0 +1,11 @@ +package StevenDimDoors.experimental; + +public interface ILinkedListNode +{ + public ILinkedListNode next(); + public ILinkedListNode prev(); + public T data(); + public void setData(T data); + public LinkedList owner(); + public T remove(); +} diff --git a/src/main/java/StevenDimDoors/experimental/LinkedList.java b/src/main/java/StevenDimDoors/experimental/LinkedList.java new file mode 100644 index 00000000..8d6b9751 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/LinkedList.java @@ -0,0 +1,236 @@ +package StevenDimDoors.experimental; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Provides an implementation of a linked list that exposes its internal nodes. + * This differs from Java's implementation, which does not expose nodes. Access + * to the nodes allows certain operations to be implemented more efficiently. + * Not all operations are supported, but we can add them as the need arises. + * @author SenseiKiwi + * + * @param The type of data to be stored in the LinkedList + */ +public class LinkedList implements Iterable +{ + private static class Node

implements ILinkedListNode

+ { + private Node

next; + private Node

prev; + private P data; + private LinkedList

owner; + + public Node(Node

prev, Node

next, P data, LinkedList

owner) + { + this.prev = prev; + this.next = next; + this.data = data; + this.owner = owner; + } + + @Override + public ILinkedListNode

next() + { + return next; + } + + @Override + public ILinkedListNode

prev() + { + return prev; + } + + @Override + public P data() + { + return data; + } + + @Override + public void setData(P data) + { + if (this == owner.header || this == owner.trailer) + { + throw new IllegalStateException("Cannot set data for the header and trailer nodes of a list."); + } + + this.data = data; + } + + @Override + public LinkedList

owner() + { + return owner; + } + + @Override + public P remove() + { + if (this == owner.header || this == owner.trailer) + { + throw new IllegalStateException("Cannot remove the header and trailer nodes of a list."); + } + + P data = this.data; + this.prev.next = this.next; + this.next.prev = this.prev; + this.owner.size--; + + this.clear(); + return data; + } + + public void clear() + { + this.data = null; + this.prev = null; + this.next = null; + this.owner = null; + } + } + + private static class LinkedListIterator

implements Iterator

+ { + private Node

current; + private Node

trailer; + + public LinkedListIterator(LinkedList

list) + { + current = list.header.next; + trailer = list.trailer; + } + + @Override + public boolean hasNext() + { + return (current != trailer); + } + + @Override + public P next() + { + if (current == trailer) + { + throw new NoSuchElementException(); + } + else + { + P result = current.data; + current = current.next; + return result; + } + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + private Node header; // Sentinel node + private Node trailer; // Sentinel node + private int size; + + public LinkedList() + { + size = 0; + header = new Node(null, null, null, this); + trailer = new Node(null, null, null, this); + header.next = trailer; + trailer.prev = header; + } + + public ILinkedListNode header() + { + return header; + } + + public ILinkedListNode trailer() + { + return trailer; + } + + public int size() + { + return size; + } + + public boolean isEmpty() + { + return (size == 0); + } + + public void clear() + { + // Go through the list and wipe everything out + Node current; + Node next; + + size = 0; + current = header.next; + while (current != trailer) + { + next = current.next; + current.clear(); + current = next; + } + header.next = trailer; + trailer.prev = header; + } + + private Node checkNode(ILinkedListNode node) + { + Node innerNode = (Node) node; + + // Check that this node actually belongs to this list instance. + // Accepting foreign nodes could corrupt the list's internal state. + if (innerNode.owner() != this) + { + throw new IllegalArgumentException("The specified node does not belong to this list."); + } + return innerNode; + } + + public ILinkedListNode addFirst(T data) + { + return addAfter(header, data); + } + + public ILinkedListNode addLast(T data) + { + return addBefore(trailer, data); + } + + public ILinkedListNode addBefore(ILinkedListNode node, T data) + { + if (node == header) + { + throw new IllegalArgumentException("Cannot add a node before the header node."); + } + return addAfter( checkNode(node).prev, data ); + } + + public ILinkedListNode addAfter(ILinkedListNode node, T data) + { + if (node == trailer) + { + throw new IllegalArgumentException("Cannot add a node after the trailer node."); + } + return addAfter( checkNode(node), data ); + } + + private Node addAfter(Node node, T data) + { + Node addition = new Node(node, node.next, data, this); + node.next = addition; + addition.next.prev = addition; + return addition; + } + + public Iterator iterator() + { + return new LinkedListIterator(this); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/MazeGenerator.java b/src/main/java/StevenDimDoors/experimental/MazeGenerator.java index ba328405..4b5f076d 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeGenerator.java +++ b/src/main/java/StevenDimDoors/experimental/MazeGenerator.java @@ -2,6 +2,9 @@ package StevenDimDoors.experimental; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; import java.util.Random; import net.minecraft.block.Block; @@ -24,24 +27,35 @@ public class MazeGenerator public static void generate(World world, int x, int y, int z, Random random) { + // Construct a random binary space partitioning of our maze volume PartitionNode root = partitionRooms(ROOT_WIDTH, ROOT_HEIGHT, ROOT_LENGTH, SPLIT_COUNT, random); - // Collect all the leaf nodes by performing a tree traversal - ArrayList rooms = new ArrayList(1 << SPLIT_COUNT); - listRooms(root, rooms); - removeRandomRooms(rooms, random); - buildRooms(root, world, new Point3D(x - ROOT_WIDTH / 2, y - ROOT_HEIGHT - 1, z - ROOT_WIDTH / 2)); + + // List all the leaf nodes of the partition tree, which denote individual rooms + ArrayList partitions = new ArrayList(1 << SPLIT_COUNT); + listRoomPartitions(root, partitions); + + // Construct an adjacency graph of the rooms we've carved out. Two rooms are + // considered adjacent if and only if a doorway could connect them. Their + // common boundary must be large enough for a doorway. + DirectedGraph rooms = createRoomGraph(root, partitions, random); + + // Cut out random subgraphs from the adjacency graph + ArrayList> cores = createMazeSections(rooms, random); + + + buildRooms(rooms, world, new Point3D(x - ROOT_WIDTH / 2, y - ROOT_HEIGHT - 1, z - ROOT_WIDTH / 2)); } - private static void listRooms(PartitionNode node, ArrayList rooms) + private static void listRoomPartitions(PartitionNode node, ArrayList partitions) { if (node.isLeaf()) { - rooms.add(node); + partitions.add(node); } else { - listRooms(node.leftChild(), rooms); - listRooms(node.rightChild(), rooms); + listRoomPartitions(node.leftChild(), partitions); + listRoomPartitions(node.rightChild(), partitions); } } @@ -135,22 +149,352 @@ public class MazeGenerator } } - private static void buildRooms(PartitionNode node, World world, Point3D offset) + private static DirectedGraph createRoomGraph(PartitionNode root, ArrayList partitions, Random random) { - if (node.isLeaf()) + DirectedGraph roomGraph = new DirectedGraph(); + HashMap> roomsToGraph = new HashMap>(2 * partitions.size()); + + // Shuffle the list of rooms so that they're not listed in any ordered way in the room graph + // This is the only convenient way of randomizing the maze sections generated later + Collections.shuffle(partitions, random); + + // Add all rooms to a graph + // Also add them to a map so we can associate rooms with their graph nodes + // The map is needed for linking graph nodes based on adjacent partitions + for (PartitionNode partition : partitions) { - buildBox(world, offset, node.minCorner(), node.maxCorner()); + roomsToGraph.put(partition, roomGraph.addNode(partition)); } - else + + // Add edges for each room + for (IGraphNode node : roomGraph.nodes()) { - if (node.leftChild() != null) - buildRooms(node.leftChild(), world, offset); - if (node.rightChild() != null) - buildRooms(node.rightChild(), world, offset); + findDoorways(node, root, roomsToGraph, roomGraph); + } + + return roomGraph; + } + + private static void findDoorways(IGraphNode roomNode, PartitionNode root, + HashMap> roomsToGraph, + DirectedGraph roomGraph) + { + // This function finds rooms adjacent to a specified room that could be connected + // to it through a doorway. Edges are added to the room graph to denote rooms that + // could be connected. The areas of their common bounds that could be carved + // out for a passage are stored in the edges. + + // Three directions have to be checked: up, forward, and right. The other three + // directions (down, back, left) aren't checked because other nodes will cover them. + // That is, down for this room is up for some other room, if it exists. Also, rooms + // are guaranteed to have at least one doorway to another room, because the minimum + // dimensions to which a room can be partitioned still allow passages along all + // its sides. A room's sibling in the partition tree is guaranteed to share a side + // through which a doorway could exist. Similar arguments guarantee the existence + // of passages such that the whole set of rooms is a connected graph - in other words, + // there will always be a way to walk from any room to any other room. + + boolean[][] detected; + PartitionNode adjacent; + + int a, b, c; + int p, q, r; + int minXI, minYI, minZI; + int maxXI, maxYI, maxZI; + Point3D otherMin; + Point3D otherMax; + DoorwayData doorway; + IGraphNode adjacentNode; + + PartitionNode room = roomNode.data(); + Point3D minCorner = room.minCorner(); + Point3D maxCorner = room.maxCorner(); + + int minX = minCorner.getX(); + int minY = minCorner.getY(); + int minZ = minCorner.getZ(); + + int maxX = maxCorner.getX(); + int maxY = maxCorner.getY(); + int maxZ = maxCorner.getZ(); + + int width = room.width(); + int height = room.height(); + int length = room.length(); + + if (maxZ < root.maxCorner().getZ()) + { + // Check for adjacent rooms along the XY plane + detected = new boolean[width][height]; + for (a = 0; a < width; a++) + { + for (b = 0; b < height; b++) + { + if (!detected[a][b]) + { + adjacent = root.findPoint(minX + a, minY + b, maxZ + 1); + if (adjacent != null) + { + // Compute the dimensions available for a doorway + otherMin = adjacent.minCorner(); + otherMax = adjacent.maxCorner(); + minXI = Math.max(minX, otherMin.getX()); + maxXI = Math.min(maxX, otherMax.getX()); + minYI = Math.max(minY, otherMin.getY()); + maxYI = Math.min(maxY, otherMax.getY()); + + for (p = a; p <= maxXI - minXI; p++) + { + for (q = b; q <= maxYI - minYI; q++) + { + detected[p][q] = true; + } + } + // Check if we meet the minimum dimensions needed for a doorway + if (maxXI - minXI + 1 >= MIN_SIDE && maxYI - minYI + 1 >= MIN_HEIGHT) + { + otherMin = new Point3D(minXI, minYI, maxZ); + otherMax = new Point3D(maxXI, maxYI, maxZ + 1); + doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Z_AXIS); + adjacentNode = roomsToGraph.get(adjacent); + roomGraph.addEdge(roomNode, adjacentNode, doorway); + } + } + else + { + detected[a][b] = true; + } + } + } + } + } + + + if (maxX < root.maxCorner().getX()) + { + // Check for adjacent rooms along the YZ plane + detected = new boolean[height][length]; + for (b = 0; b < height; b++) + { + for (c = 0; c < length; c++) + { + if (!detected[b][c]) + { + adjacent = root.findPoint(maxX + 1, minY + b, minZ + c); + if (adjacent != null) + { + // Compute the dimensions available for a doorway + otherMin = adjacent.minCorner(); + otherMax = adjacent.maxCorner(); + minYI = Math.max(minY, otherMin.getY()); + maxYI = Math.min(maxY, otherMax.getY()); + minZI = Math.max(minZ, otherMin.getZ()); + maxZI = Math.min(maxZ, otherMax.getZ()); + + for (q = b; q <= maxYI - minYI; q++) + { + for (r = c; r <= maxZI - minZI; r++) + { + detected[q][r] = true; + } + } + // Check if we meet the minimum dimensions needed for a doorway + if (maxYI - minYI + 1 >= MIN_HEIGHT && maxZI - minZI + 1 >= MIN_SIDE) + { + otherMin = new Point3D(maxX, minYI, minZI); + otherMax = new Point3D(maxX + 1, maxYI, maxZI); + doorway = new DoorwayData(otherMin, otherMax, DoorwayData.X_AXIS); + adjacentNode = roomsToGraph.get(adjacent); + roomGraph.addEdge(roomNode, adjacentNode, doorway); + } + } + else + { + detected[b][c] = true; + } + } + } + } + } + + + if (maxY < root.maxCorner().getY()) + { + // Check for adjacent rooms along the XZ plane + detected = new boolean[width][length]; + for (a = 0; a < width; a++) + { + for (c = 0; c < length; c++) + { + if (!detected[a][c]) + { + adjacent = root.findPoint(minX + a, maxY + 1, minZ + c); + if (adjacent != null) + { + // Compute the dimensions available for a doorway + otherMin = adjacent.minCorner(); + otherMax = adjacent.maxCorner(); + minXI = Math.max(minX, otherMin.getX()); + maxXI = Math.min(maxX, otherMax.getX()); + minZI = Math.max(minZ, otherMin.getZ()); + maxZI = Math.min(maxZ, otherMax.getZ()); + + for (p = a; p <= maxXI - minXI; p++) + { + for (r = c; r <= maxZI - minZI; r++) + { + detected[p][r] = true; + } + } + // Check if we meet the minimum dimensions needed for a doorway + if (maxXI - minXI + 1 >= MIN_SIDE && maxZI - minZI + 1 >= MIN_SIDE) + { + otherMin = new Point3D(minXI, maxY, minZI); + otherMax = new Point3D(maxXI, maxY + 1, maxZI); + doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Y_AXIS); + adjacentNode = roomsToGraph.get(adjacent); + roomGraph.addEdge(roomNode, adjacentNode, doorway); + } + } + else + { + detected[a][c] = true; + } + } + } + } + } + + //Done! + } + + private static ArrayList> createMazeSections(DirectedGraph roomGraph, Random random) + { + // The randomness of the sections generated here hinges on + // the nodes in the graph being in a random order. We assume + // that was handled in a previous step! + + final int MAX_DISTANCE = 2; + + int distance; + IGraphNode current; + IGraphNode neighbor; + + ArrayList> cores = new ArrayList>(); + Queue> ordering = new LinkedList>(); + HashMap, Integer> distances = new HashMap, Integer>(); + + // Repeatedly generate sections until all nodes have been visited + for (IGraphNode node : roomGraph.nodes()) + { + // If this node has an indegree and outdegree of 0, then it has no neighbors, + // which means it could not have been visited. This could happen if its neighbors + // were pruned away before. Single rooms look weird, so remove it. + if (node.indegree() == 0 && node.outdegree() == 0) + { + roomGraph.removeNode(node); + } + + // If this node hasn't been visited, then use it as the core of a new section + // Otherwise, ignore it, since it already belongs to a section + else if (!distances.containsKey(node)) + { + cores.add(node); + + // Perform a breadth-first search to tag surrounding nodes with distances + distances.put(node, 0); + ordering.add(node); + while (!ordering.isEmpty()) + { + current = ordering.remove(); + distance = distances.get(current) + 1; + + if (distance <= MAX_DISTANCE + 1) + { + // Visit neighboring nodes and assign them distances, if they don't + // have a distance assigned already + for (IEdge edge : current.inbound()) + { + neighbor = edge.head(); + if (!distances.containsKey(neighbor)) + { + distances.put(neighbor, distance); + ordering.add(neighbor); + } + } + for (IEdge edge : current.outbound()) + { + neighbor = edge.tail(); + if (!distances.containsKey(neighbor)) + { + distances.put(neighbor, distance); + ordering.add(neighbor); + } + } + } + else + { + roomGraph.removeNode(current); + break; + } + } + + // Remove all nodes that have a distance of exactly MAX_DISTANCE + 1 + // Those are precisely the nodes that remain in the queue + while (!ordering.isEmpty()) + { + roomGraph.removeNode( ordering.remove() ); + } + } + } + return cores; + } + + private static void buildRooms(DirectedGraph roomGraph, World world, Point3D offset) + { + for (IGraphNode node : roomGraph.nodes()) + { + PartitionNode room = node.data(); + buildBox(world, offset, room.minCorner(), room.maxCorner(), Block.stoneBrick.blockID, 0); + } + + // TESTING!!! + // This code carves out cheap doorways + // The final system will be better + // This has to happen after all the rooms have been built or the passages will be overwritten sometimes + for (IGraphNode node : roomGraph.nodes()) + { + for (IEdge doorway : node.outbound()) + { + char axis = doorway.data().axis(); + Point3D lower = doorway.data().minCorner(); + + if (axis == DoorwayData.Z_AXIS) + { + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 1, offset.getZ() + lower.getZ(), 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 2, offset.getZ() + lower.getZ(), 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 1, offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 2, offset.getZ() + lower.getZ() + 1, 0, 0); + } + else if (axis == DoorwayData.X_AXIS) + { + setBlockDirectly(world, offset.getX() + lower.getX(), offset.getY() + lower.getY() + 1, offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX(), offset.getY() + lower.getY() + 2, offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 1, offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 2, offset.getZ() + lower.getZ() + 1, 0, 0); + } + else + { + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY(), offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY(), offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 1, offset.getZ() + lower.getZ() + 1, 0, 0); + setBlockDirectly(world, offset.getX() + lower.getX() + 1, offset.getY() + lower.getY() + 1, offset.getZ() + lower.getZ() + 1, 0, 0); + } + } } } - private static void buildBox(World world, Point3D offset, Point3D minCorner, Point3D maxCorner) + private static void buildBox(World world, Point3D offset, Point3D minCorner, Point3D maxCorner, int blockID, int metadata) { int minX = minCorner.getX() + offset.getX(); int minY = minCorner.getY() + offset.getY(); @@ -161,30 +505,29 @@ public class MazeGenerator int maxZ = maxCorner.getZ() + offset.getZ(); int x, y, z; - int blockID = Block.stoneBrick.blockID; for (x = minX; x <= maxX; x++) { for (z = minZ; z <= maxZ; z++) { - setBlockDirectly(world, x, minY, z, blockID, 0); - setBlockDirectly(world, x, maxY, z, blockID, 0); + setBlockDirectly(world, x, minY, z, blockID, metadata); + setBlockDirectly(world, x, maxY, z, blockID, metadata); } } for (x = minX; x <= maxX; x++) { for (y = minY; y <= maxY; y++) { - setBlockDirectly(world, x, y, minZ, blockID, 0); - setBlockDirectly(world, x, y, maxZ, blockID, 0); + setBlockDirectly(world, x, y, minZ, blockID, metadata); + setBlockDirectly(world, x, y, maxZ, blockID, metadata); } } for (z = minZ; z <= maxZ; z++) { for (y = minY; y <= maxY; y++) { - setBlockDirectly(world, minX, y, z, blockID, 0); - setBlockDirectly(world, maxX, y, z, blockID, 0); + setBlockDirectly(world, minX, y, z, blockID, metadata); + setBlockDirectly(world, maxX, y, z, blockID, metadata); } } } diff --git a/src/main/java/StevenDimDoors/experimental/PartitionNode.java b/src/main/java/StevenDimDoors/experimental/PartitionNode.java index 9c14eb01..25d8d6d1 100644 --- a/src/main/java/StevenDimDoors/experimental/PartitionNode.java +++ b/src/main/java/StevenDimDoors/experimental/PartitionNode.java @@ -122,4 +122,40 @@ public class PartitionNode parent = null; } } + + public boolean contains(int x, int y, int z) + { + return ((minCorner.getX() <= x && x <= maxCorner.getX()) && + (minCorner.getY() <= y && y <= maxCorner.getY()) && + (minCorner.getZ() <= z && z <= maxCorner.getZ())); + } + + public PartitionNode findPoint(int x, int y, int z) + { + // Find the lowest node that contains the specified point or return null + if (this.contains(x, y, z)) + { + return this.findPointInternal(x, y, z); + } + else + { + return null; + } + } + + private PartitionNode findPointInternal(int x, int y, int z) + { + if (leftChild != null && leftChild.contains(x, y, z)) + { + return leftChild.findPointInternal(x, y, z); + } + else if (rightChild != null && rightChild.contains(x, y, z)) + { + return rightChild.findPointInternal(x, y, z); + } + else + { + return this; + } + } } diff --git a/src/main/java/StevenDimDoors/experimental/RoomNode.java b/src/main/java/StevenDimDoors/experimental/RoomNode.java deleted file mode 100644 index 78a70027..00000000 --- a/src/main/java/StevenDimDoors/experimental/RoomNode.java +++ /dev/null @@ -1,56 +0,0 @@ -package StevenDimDoors.experimental; - -import java.util.ArrayList; - -public class RoomNode -{ - private ArrayList outbound; - private ArrayList inbound; - private PartitionNode bounds; - private int distance; - private boolean visited; - - public RoomNode(PartitionNode bounds) - { - this.bounds = bounds; - this.distance = 0; - this.visited = false; - this.outbound = new ArrayList(); - this.inbound = new ArrayList(); - } - - public int distance() - { - return distance; - } - - public boolean isVisited() - { - return visited; - } - - public void setDistance(int value) - { - distance = value; - } - - public void setVisited(boolean value) - { - visited = value; - } - - public PartitionNode bounds() - { - return bounds; - } - - public void addInboundDoorway(DoorwayData data) - { - inbound.add(data); - } - - public void addOutboundDoorway(DoorwayData data) - { - outbound.add(data); - } -}