Mazes #155
@@ -18,8 +18,8 @@ public class MazeBuilder
|
|||||||
Point3D offset = new Point3D(x - design.width() / 2, y - design.height() - 1, z - design.length() / 2);
|
Point3D offset = new Point3D(x - design.width() / 2, y - design.height() - 1, z - design.length() / 2);
|
||||||
SphereDecayOperation decay = new SphereDecayOperation(random, 0, 0, Block.stoneBrick.blockID, 2);
|
SphereDecayOperation decay = new SphereDecayOperation(random, 0, 0, Block.stoneBrick.blockID, 2);
|
||||||
|
|
||||||
buildRooms(design.getRoomGraph(), world, offset);
|
buildRooms(design.getLayout(), world, offset);
|
||||||
carveDoorways(design.getRoomGraph(), world, offset, decay, random);
|
carveDoorways(design.getLayout(), world, offset, decay, random);
|
||||||
|
|
||||||
//placeDoors(design, world, offset);
|
//placeDoors(design, world, offset);
|
||||||
|
|
||||||
@@ -32,25 +32,25 @@ public class MazeBuilder
|
|||||||
//final int DECAY_BOX_SIZE = 8
|
//final int DECAY_BOX_SIZE = 8
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void buildRooms(DirectedGraph<PartitionNode, DoorwayData> roomGraph, World world, Point3D offset)
|
private static void buildRooms(DirectedGraph<RoomData, DoorwayData> layout, World world, Point3D offset)
|
||||||
{
|
{
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
|
||||||
{
|
{
|
||||||
PartitionNode room = node.data();
|
PartitionNode room = node.data().getPartitionNode();
|
||||||
buildBox(world, offset, room.minCorner(), room.maxCorner(), Block.stoneBrick.blockID, 0);
|
buildBox(world, offset, room.minCorner(), room.maxCorner(), Block.stoneBrick.blockID, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void carveDoorways(DirectedGraph<PartitionNode, DoorwayData> roomGraph, World world,
|
private static void carveDoorways(DirectedGraph<RoomData, DoorwayData> layout, World world,
|
||||||
Point3D offset, SphereDecayOperation decay, Random random)
|
Point3D offset, SphereDecayOperation decay, Random random)
|
||||||
{
|
{
|
||||||
char axis;
|
char axis;
|
||||||
Point3D lower;
|
Point3D lower;
|
||||||
DoorwayData doorway;
|
DoorwayData doorway;
|
||||||
|
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
|
||||||
{
|
{
|
||||||
for (IEdge<PartitionNode, DoorwayData> passage : node.outbound())
|
for (IEdge<RoomData, DoorwayData> passage : node.outbound())
|
||||||
{
|
{
|
||||||
doorway = passage.data();
|
doorway = passage.data();
|
||||||
axis = doorway.axis();
|
axis = doorway.axis();
|
||||||
@@ -166,7 +166,6 @@ public class MazeBuilder
|
|||||||
setBlockDirectly(world, x, y + 1, z, 0, 0);
|
setBlockDirectly(world, x, y + 1, z, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void buildBox(World world, Point3D offset, Point3D minCorner, Point3D maxCorner, int blockID, int metadata)
|
private static void buildBox(World world, Point3D offset, Point3D minCorner, Point3D maxCorner, int blockID, int metadata)
|
||||||
{
|
{
|
||||||
int minX = minCorner.getX() + offset.getX();
|
int minX = minCorner.getX() + offset.getX();
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import java.util.ArrayList;
|
|||||||
public class MazeDesign
|
public class MazeDesign
|
||||||
{
|
{
|
||||||
private PartitionNode root;
|
private PartitionNode root;
|
||||||
private DirectedGraph<PartitionNode, DoorwayData> rooms;
|
private DirectedGraph<RoomData, DoorwayData> layout;
|
||||||
private ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores;
|
|
||||||
private ArrayList<BoundingBox> protectedAreas;
|
|
||||||
|
|
||||||
public MazeDesign(PartitionNode root, DirectedGraph<PartitionNode, DoorwayData> rooms,
|
public MazeDesign(PartitionNode root, DirectedGraph<RoomData, DoorwayData> layout)
|
||||||
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores)
|
|
||||||
{
|
{
|
||||||
this.root = root;
|
this.root = root;
|
||||||
this.rooms = rooms;
|
this.layout = layout;
|
||||||
this.cores = cores;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PartitionNode getRootPartition()
|
public PartitionNode getRootPartition()
|
||||||
@@ -22,19 +18,9 @@ public class MazeDesign
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DirectedGraph<PartitionNode, DoorwayData> getRoomGraph()
|
public DirectedGraph<RoomData, DoorwayData> getLayout()
|
||||||
{
|
{
|
||||||
return rooms;
|
return layout;
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<IGraphNode<PartitionNode, DoorwayData>> getCoreNodes()
|
|
||||||
{
|
|
||||||
return cores;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<BoundingBox> getProtectedAreas()
|
|
||||||
{
|
|
||||||
return protectedAreas;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int width()
|
public int width()
|
||||||
|
|||||||
@@ -25,55 +25,66 @@ public class MazeDesigner
|
|||||||
public static MazeDesign generate(Random random)
|
public static MazeDesign generate(Random random)
|
||||||
{
|
{
|
||||||
// Construct a random binary space partitioning of our maze volume
|
// Construct a random binary space partitioning of our maze volume
|
||||||
PartitionNode root = partitionRooms(MAZE_WIDTH, MAZE_HEIGHT, MAZE_LENGTH, SPLIT_COUNT, random);
|
PartitionNode<RoomData> root = partitionRooms(MAZE_WIDTH, MAZE_HEIGHT, MAZE_LENGTH, SPLIT_COUNT, random);
|
||||||
|
|
||||||
// List all the leaf nodes of the partition tree, which denote individual rooms
|
// Attach rooms to all the leaf nodes of the partition tree
|
||||||
ArrayList<PartitionNode> partitions = new ArrayList<PartitionNode>(1 << SPLIT_COUNT);
|
ArrayList<RoomData> rooms = new ArrayList<RoomData>(1 << SPLIT_COUNT);
|
||||||
listRoomPartitions(root, partitions);
|
attachRooms(root, rooms);
|
||||||
|
|
||||||
|
// 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(rooms, random);
|
||||||
|
|
||||||
// Construct an adjacency graph of the rooms we've carved out. Two rooms are
|
// 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
|
// considered adjacent if and only if a doorway could connect them. Their
|
||||||
// common boundary must be large enough for a doorway.
|
// common boundary must be large enough for a doorway.
|
||||||
DirectedGraph<PartitionNode, DoorwayData> rooms = createRoomGraph(root, partitions, random);
|
DirectedGraph<RoomData, DoorwayData> layout = createRoomGraph(root, rooms, random);
|
||||||
|
|
||||||
// Cut out random subgraphs from the adjacency graph
|
// Cut out random subgraphs from the adjacency graph
|
||||||
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores = createMazeSections(rooms, random);
|
ArrayList<RoomData> cores = createMazeSections(layout, random);
|
||||||
|
|
||||||
// Remove unnecessary passages through floors/ceilings and some from the walls
|
// Remove unnecessary passages through floors/ceilings and some from the walls
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> core : cores)
|
for (RoomData core : cores)
|
||||||
{
|
{
|
||||||
pruneDoorways(core, rooms, random);
|
pruneDoorways(core.getLayoutNode(), layout, random);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MazeDesign(root, rooms, cores);
|
return new MazeDesign(root, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void listRoomPartitions(PartitionNode node, ArrayList<PartitionNode> partitions)
|
private static void attachRooms(PartitionNode<RoomData> node, ArrayList<RoomData> partitions)
|
||||||
{
|
{
|
||||||
if (node.isLeaf())
|
if (node.isLeaf())
|
||||||
{
|
{
|
||||||
partitions.add(node);
|
partitions.add(new RoomData(node));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listRoomPartitions(node.leftChild(), partitions);
|
attachRooms(node.leftChild(), partitions);
|
||||||
listRoomPartitions(node.rightChild(), partitions);
|
attachRooms(node.rightChild(), partitions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void removeRoomPartitions(PartitionNode node)
|
private static void removeRoom(RoomData room, DirectedGraph<RoomData, DoorwayData> layout)
|
||||||
{
|
{
|
||||||
// Remove a node and any of its ancestors that become leaf nodes
|
// Remove the room from the partition tree and from the layout graph.
|
||||||
|
// Also remove any ancestors that become leaf nodes.
|
||||||
PartitionNode parent;
|
PartitionNode parent;
|
||||||
PartitionNode current;
|
PartitionNode current;
|
||||||
|
|
||||||
current = node;
|
current = room.getPartitionNode();
|
||||||
while (current != null && current.isLeaf())
|
while (current != null && current.isLeaf())
|
||||||
{
|
{
|
||||||
parent = current.parent();
|
parent = current.parent();
|
||||||
current.remove();
|
current.remove();
|
||||||
current = parent;
|
current = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the room from the layout graph
|
||||||
|
layout.removeNode(room.getLayoutNode());
|
||||||
|
|
||||||
|
// Wipe the room's data, as a precaution.
|
||||||
|
room.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PartitionNode partitionRooms(int width, int height, int length, int maxLevels, Random random)
|
private static PartitionNode partitionRooms(int width, int height, int length, int maxLevels, Random random)
|
||||||
@@ -140,35 +151,25 @@ public class MazeDesigner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DirectedGraph<PartitionNode, DoorwayData> createRoomGraph(PartitionNode root, ArrayList<PartitionNode> partitions, Random random)
|
private static DirectedGraph<RoomData, DoorwayData> createRoomGraph(PartitionNode<RoomData> root, ArrayList<RoomData> rooms, Random random)
|
||||||
{
|
{
|
||||||
DirectedGraph<PartitionNode, DoorwayData> roomGraph = new DirectedGraph<PartitionNode, DoorwayData>();
|
DirectedGraph<RoomData, DoorwayData> layout = new DirectedGraph<RoomData, DoorwayData>();
|
||||||
HashMap<PartitionNode, IGraphNode<PartitionNode, DoorwayData>> roomsToGraph = new HashMap<PartitionNode, IGraphNode<PartitionNode, DoorwayData>>(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
|
// Add all rooms to a graph
|
||||||
// Also add them to a map so we can associate rooms with their graph nodes
|
for (RoomData room : rooms)
|
||||||
// The map is needed for linking graph nodes based on adjacent partitions
|
|
||||||
for (PartitionNode partition : partitions)
|
|
||||||
{
|
{
|
||||||
roomsToGraph.put(partition, roomGraph.addNode(partition));
|
room.addToLayout(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add edges for each room
|
// Add edges for each room
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
|
||||||
{
|
{
|
||||||
findDoorways(node, root, roomsToGraph, roomGraph);
|
findDoorways(node.data(), root, layout);
|
||||||
|
}
|
||||||
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
return roomGraph;
|
private static void findDoorways(RoomData room, PartitionNode<RoomData> root,
|
||||||
}
|
DirectedGraph<RoomData, DoorwayData> layout)
|
||||||
|
|
||||||
private static void findDoorways(IGraphNode<PartitionNode, DoorwayData> roomNode, PartitionNode root,
|
|
||||||
HashMap<PartitionNode, IGraphNode<PartitionNode, DoorwayData>> roomsToGraph,
|
|
||||||
DirectedGraph<PartitionNode, DoorwayData> roomGraph)
|
|
||||||
{
|
{
|
||||||
// This function finds rooms adjacent to a specified room that could be connected
|
// 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
|
// to it through a doorway. Edges are added to the room graph to denote rooms that
|
||||||
@@ -186,7 +187,7 @@ public class MazeDesigner
|
|||||||
// there will always be a way to walk from any room to any other room.
|
// there will always be a way to walk from any room to any other room.
|
||||||
|
|
||||||
boolean[][] detected;
|
boolean[][] detected;
|
||||||
PartitionNode adjacent;
|
PartitionNode<RoomData> adjacent;
|
||||||
|
|
||||||
int a, b, c;
|
int a, b, c;
|
||||||
int p, q, r;
|
int p, q, r;
|
||||||
@@ -195,11 +196,10 @@ public class MazeDesigner
|
|||||||
Point3D otherMin;
|
Point3D otherMin;
|
||||||
Point3D otherMax;
|
Point3D otherMax;
|
||||||
DoorwayData doorway;
|
DoorwayData doorway;
|
||||||
IGraphNode<PartitionNode, DoorwayData> adjacentNode;
|
|
||||||
|
|
||||||
PartitionNode room = roomNode.data();
|
PartitionNode partition = room.getPartitionNode();
|
||||||
Point3D minCorner = room.minCorner();
|
Point3D minCorner = partition.minCorner();
|
||||||
Point3D maxCorner = room.maxCorner();
|
Point3D maxCorner = partition.maxCorner();
|
||||||
|
|
||||||
int minX = minCorner.getX();
|
int minX = minCorner.getX();
|
||||||
int minY = minCorner.getY();
|
int minY = minCorner.getY();
|
||||||
@@ -209,9 +209,9 @@ public class MazeDesigner
|
|||||||
int maxY = maxCorner.getY();
|
int maxY = maxCorner.getY();
|
||||||
int maxZ = maxCorner.getZ();
|
int maxZ = maxCorner.getZ();
|
||||||
|
|
||||||
int width = room.width();
|
int width = partition.width();
|
||||||
int height = room.height();
|
int height = partition.height();
|
||||||
int length = room.length();
|
int length = partition.length();
|
||||||
|
|
||||||
if (maxZ < root.maxCorner().getZ())
|
if (maxZ < root.maxCorner().getZ())
|
||||||
{
|
{
|
||||||
@@ -247,8 +247,7 @@ public class MazeDesigner
|
|||||||
otherMin = new Point3D(minXI, minYI, maxZ);
|
otherMin = new Point3D(minXI, minYI, maxZ);
|
||||||
otherMax = new Point3D(maxXI, maxYI, maxZ + 1);
|
otherMax = new Point3D(maxXI, maxYI, maxZ + 1);
|
||||||
doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Z_AXIS);
|
doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Z_AXIS);
|
||||||
adjacentNode = roomsToGraph.get(adjacent);
|
layout.addEdge(room.getLayoutNode(), adjacent.getData().getLayoutNode(), doorway);
|
||||||
roomGraph.addEdge(roomNode, adjacentNode, doorway);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -295,8 +294,7 @@ public class MazeDesigner
|
|||||||
otherMin = new Point3D(maxX, minYI, minZI);
|
otherMin = new Point3D(maxX, minYI, minZI);
|
||||||
otherMax = new Point3D(maxX + 1, maxYI, maxZI);
|
otherMax = new Point3D(maxX + 1, maxYI, maxZI);
|
||||||
doorway = new DoorwayData(otherMin, otherMax, DoorwayData.X_AXIS);
|
doorway = new DoorwayData(otherMin, otherMax, DoorwayData.X_AXIS);
|
||||||
adjacentNode = roomsToGraph.get(adjacent);
|
layout.addEdge(room.getLayoutNode(), adjacent.getData().getLayoutNode(), doorway);
|
||||||
roomGraph.addEdge(roomNode, adjacentNode, doorway);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -343,8 +341,7 @@ public class MazeDesigner
|
|||||||
otherMin = new Point3D(minXI, maxY, minZI);
|
otherMin = new Point3D(minXI, maxY, minZI);
|
||||||
otherMax = new Point3D(maxXI, maxY + 1, maxZI);
|
otherMax = new Point3D(maxXI, maxY + 1, maxZI);
|
||||||
doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Y_AXIS);
|
doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Y_AXIS);
|
||||||
adjacentNode = roomsToGraph.get(adjacent);
|
layout.addEdge(room.getLayoutNode(), adjacent.getData().getLayoutNode(), doorway);
|
||||||
roomGraph.addEdge(roomNode, adjacentNode, doorway);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -359,7 +356,7 @@ public class MazeDesigner
|
|||||||
//Done!
|
//Done!
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ArrayList<IGraphNode<PartitionNode, DoorwayData>> createMazeSections(DirectedGraph<PartitionNode, DoorwayData> roomGraph, Random random)
|
private static ArrayList<RoomData> createMazeSections(DirectedGraph<RoomData, DoorwayData> layout, Random random)
|
||||||
{
|
{
|
||||||
// The randomness of the sections generated here hinges on
|
// The randomness of the sections generated here hinges on
|
||||||
// the nodes in the graph being in a random order. We assume
|
// the nodes in the graph being in a random order. We assume
|
||||||
@@ -369,66 +366,68 @@ public class MazeDesigner
|
|||||||
final int MIN_SECTION_ROOMS = 5;
|
final int MIN_SECTION_ROOMS = 5;
|
||||||
|
|
||||||
int distance;
|
int distance;
|
||||||
IGraphNode<PartitionNode, DoorwayData> current;
|
RoomData room;
|
||||||
IGraphNode<PartitionNode, DoorwayData> neighbor;
|
RoomData neighbor;
|
||||||
|
IGraphNode<RoomData, DoorwayData> current;
|
||||||
|
|
||||||
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>();
|
ArrayList<RoomData> cores = new ArrayList<RoomData>();
|
||||||
ArrayList<IGraphNode<PartitionNode, DoorwayData>> removals = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>();
|
ArrayList<RoomData> removals = new ArrayList<RoomData>();
|
||||||
ArrayList<IGraphNode<PartitionNode, DoorwayData>> section = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>();
|
ArrayList<RoomData> section = new ArrayList<RoomData>();
|
||||||
|
|
||||||
Queue<IGraphNode<PartitionNode, DoorwayData>> ordering = new LinkedList<IGraphNode<PartitionNode, DoorwayData>>();
|
Queue<RoomData> ordering = new LinkedList<RoomData>();
|
||||||
HashMap<IGraphNode<PartitionNode, DoorwayData>, Integer> distances = new HashMap<IGraphNode<PartitionNode, DoorwayData>, Integer>();
|
|
||||||
|
|
||||||
// Repeatedly generate sections until all nodes have been visited
|
// Repeatedly generate sections until all nodes have been visited
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
|
||||||
{
|
{
|
||||||
// If this node hasn't been visited, then use it as the core of a new section
|
// If this room hasn't been visited (distance = -1), then use it as the core of a new section
|
||||||
// Otherwise, ignore it, since it was already processed
|
// Otherwise, ignore it, since it was already processed
|
||||||
if (!distances.containsKey(node))
|
room = node.data();
|
||||||
|
if (room.getDistance() < 0)
|
||||||
{
|
{
|
||||||
// Perform a breadth-first search to tag surrounding nodes with distances
|
// Perform a breadth-first search to tag surrounding nodes with distances
|
||||||
distances.put(node, 0);
|
room.setDistance(0);
|
||||||
ordering.add(node);
|
ordering.add(room);
|
||||||
section.clear();
|
section.clear();
|
||||||
|
|
||||||
while (!ordering.isEmpty())
|
while (!ordering.isEmpty())
|
||||||
{
|
{
|
||||||
current = ordering.remove();
|
room = ordering.remove();
|
||||||
distance = distances.get(current) + 1;
|
distance = room.getDistance() + 1;
|
||||||
|
|
||||||
if (distance <= MAX_DISTANCE + 1)
|
if (distance <= MAX_DISTANCE + 1)
|
||||||
{
|
{
|
||||||
section.add(current);
|
section.add(room);
|
||||||
|
current = room.getLayoutNode();
|
||||||
|
|
||||||
// Visit neighboring nodes and assign them distances, if they don't
|
// Visit neighboring rooms and assign them distances, if they don't
|
||||||
// have a distance assigned already
|
// have a proper distance assigned already
|
||||||
for (IEdge<PartitionNode, DoorwayData> edge : current.inbound())
|
for (IEdge<RoomData, DoorwayData> edge : current.inbound())
|
||||||
{
|
{
|
||||||
neighbor = edge.head();
|
neighbor = edge.head().data();
|
||||||
if (!distances.containsKey(neighbor))
|
if (neighbor.getDistance() < 0)
|
||||||
{
|
{
|
||||||
distances.put(neighbor, distance);
|
neighbor.setDistance(distance);
|
||||||
ordering.add(neighbor);
|
ordering.add(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (IEdge<PartitionNode, DoorwayData> edge : current.outbound())
|
for (IEdge<RoomData, DoorwayData> edge : current.outbound())
|
||||||
{
|
{
|
||||||
neighbor = edge.tail();
|
neighbor = edge.tail().data();
|
||||||
if (!distances.containsKey(neighbor))
|
if (neighbor.getDistance() < 0)
|
||||||
{
|
{
|
||||||
distances.put(neighbor, distance);
|
neighbor.setDistance(distance);
|
||||||
ordering.add(neighbor);
|
ordering.add(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
removals.add(current);
|
removals.add(room);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// List nodes that have a distance of exactly MAX_DISTANCE + 1
|
// List rooms that have a distance of exactly MAX_DISTANCE + 1
|
||||||
// Those are precisely the nodes that remain in the queue
|
// Those are precisely the nodes that remain in the queue
|
||||||
// We can't remove them immediately because that could break
|
// We can't remove them immediately because that could break
|
||||||
// the iterator for the graph.
|
// the iterator for the graph.
|
||||||
@@ -440,7 +439,7 @@ public class MazeDesigner
|
|||||||
// Check if this section contains enough rooms
|
// Check if this section contains enough rooms
|
||||||
if (section.size() >= MIN_SECTION_ROOMS)
|
if (section.size() >= MIN_SECTION_ROOMS)
|
||||||
{
|
{
|
||||||
cores.add(node);
|
cores.add(node.data());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -449,18 +448,17 @@ public class MazeDesigner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all the nodes that were listed for removal
|
// Remove all the rooms that were listed for removal
|
||||||
// Also remove unused partitions from the partition tree
|
// Also remove unused partitions from the partition tree
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> node : removals)
|
for (RoomData target : removals)
|
||||||
{
|
{
|
||||||
removeRoomPartitions(node.data());
|
removeRoom(target, layout);
|
||||||
roomGraph.removeNode(node);
|
|
||||||
}
|
}
|
||||||
return cores;
|
return cores;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void pruneDoorways(IGraphNode<PartitionNode, DoorwayData> core,
|
private static void pruneDoorways(IGraphNode<RoomData, DoorwayData> core,
|
||||||
DirectedGraph<PartitionNode, DoorwayData> rooms, Random random)
|
DirectedGraph<RoomData, DoorwayData> layout, Random random)
|
||||||
{
|
{
|
||||||
// We receive a node for one of the rooms in a section of the maze
|
// We receive a node for one of the rooms in a section of the maze
|
||||||
// and we need to remove as many floor doorways as possible while
|
// and we need to remove as many floor doorways as possible while
|
||||||
@@ -478,12 +476,12 @@ public class MazeDesigner
|
|||||||
// idea applies for the other doorways, plus some randomness.
|
// idea applies for the other doorways, plus some randomness.
|
||||||
|
|
||||||
// First, list all nodes in the subgraph
|
// First, list all nodes in the subgraph
|
||||||
IGraphNode<PartitionNode, DoorwayData> current;
|
IGraphNode<RoomData, DoorwayData> current;
|
||||||
IGraphNode<PartitionNode, DoorwayData> neighbor;
|
IGraphNode<RoomData, DoorwayData> neighbor;
|
||||||
|
|
||||||
Stack<IGraphNode<PartitionNode, DoorwayData>> ordering = new Stack<IGraphNode<PartitionNode, DoorwayData>>();
|
Stack<IGraphNode<RoomData, DoorwayData>> ordering = new Stack<IGraphNode<RoomData, DoorwayData>>();
|
||||||
ArrayList<IGraphNode<PartitionNode, DoorwayData>> subgraph = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>(64);
|
ArrayList<IGraphNode<RoomData, DoorwayData>> subgraph = new ArrayList<IGraphNode<RoomData, DoorwayData>>(64);
|
||||||
DisjointSet<IGraphNode<PartitionNode, DoorwayData>> components = new DisjointSet<IGraphNode<PartitionNode, DoorwayData>>(128);
|
DisjointSet<IGraphNode<RoomData, DoorwayData>> components = new DisjointSet<IGraphNode<RoomData, DoorwayData>>(128);
|
||||||
|
|
||||||
ordering.add(core);
|
ordering.add(core);
|
||||||
components.makeSet(core);
|
components.makeSet(core);
|
||||||
@@ -492,7 +490,7 @@ public class MazeDesigner
|
|||||||
current = ordering.pop();
|
current = ordering.pop();
|
||||||
subgraph.add(current);
|
subgraph.add(current);
|
||||||
|
|
||||||
for (IEdge<PartitionNode, DoorwayData> edge : current.inbound())
|
for (IEdge<RoomData, DoorwayData> edge : current.inbound())
|
||||||
{
|
{
|
||||||
neighbor = edge.head();
|
neighbor = edge.head();
|
||||||
if (components.makeSet(neighbor))
|
if (components.makeSet(neighbor))
|
||||||
@@ -500,7 +498,7 @@ public class MazeDesigner
|
|||||||
ordering.add(neighbor);
|
ordering.add(neighbor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (IEdge<PartitionNode, DoorwayData> edge : current.outbound())
|
for (IEdge<RoomData, DoorwayData> edge : current.outbound())
|
||||||
{
|
{
|
||||||
neighbor = edge.tail();
|
neighbor = edge.tail();
|
||||||
if (components.makeSet(neighbor))
|
if (components.makeSet(neighbor))
|
||||||
@@ -513,12 +511,12 @@ public class MazeDesigner
|
|||||||
// Now iterate over the list of nodes and merge their sets
|
// Now iterate over the list of nodes and merge their sets
|
||||||
// We only have to look at outbound edges since inbound edges mirror them
|
// We only have to look at outbound edges since inbound edges mirror them
|
||||||
// Also list any Y_AXIS doorways we come across
|
// Also list any Y_AXIS doorways we come across
|
||||||
ArrayList<IEdge<PartitionNode, DoorwayData>> targets =
|
ArrayList<IEdge<RoomData, DoorwayData>> targets =
|
||||||
new ArrayList<IEdge<PartitionNode, DoorwayData>>();
|
new ArrayList<IEdge<RoomData, DoorwayData>>();
|
||||||
|
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
|
for (IGraphNode<RoomData, DoorwayData> room : subgraph)
|
||||||
{
|
{
|
||||||
for (IEdge<PartitionNode, DoorwayData> passage : room.outbound())
|
for (IEdge<RoomData, DoorwayData> passage : room.outbound())
|
||||||
{
|
{
|
||||||
if (passage.data().axis() != DoorwayData.Y_AXIS)
|
if (passage.data().axis() != DoorwayData.Y_AXIS)
|
||||||
{
|
{
|
||||||
@@ -535,11 +533,11 @@ public class MazeDesigner
|
|||||||
Collections.shuffle(targets, random);
|
Collections.shuffle(targets, random);
|
||||||
|
|
||||||
// Merge sets together and remove unnecessary doorways
|
// Merge sets together and remove unnecessary doorways
|
||||||
for (IEdge<PartitionNode, DoorwayData> passage : targets)
|
for (IEdge<RoomData, DoorwayData> passage : targets)
|
||||||
{
|
{
|
||||||
if (!components.mergeSets(passage.head(), passage.tail()))
|
if (!components.mergeSets(passage.head(), passage.tail()))
|
||||||
{
|
{
|
||||||
rooms.removeEdge(passage);
|
layout.removeEdge(passage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,13 +546,13 @@ public class MazeDesigner
|
|||||||
components.clear();
|
components.clear();
|
||||||
targets.clear();
|
targets.clear();
|
||||||
|
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
|
for (IGraphNode<RoomData, DoorwayData> room : subgraph)
|
||||||
{
|
{
|
||||||
components.makeSet(room);
|
components.makeSet(room);
|
||||||
}
|
}
|
||||||
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
|
for (IGraphNode<RoomData, DoorwayData> room : subgraph)
|
||||||
{
|
{
|
||||||
for (IEdge<PartitionNode, DoorwayData> passage : room.outbound())
|
for (IEdge<RoomData, DoorwayData> passage : room.outbound())
|
||||||
{
|
{
|
||||||
if (passage.data().axis() == DoorwayData.Y_AXIS)
|
if (passage.data().axis() == DoorwayData.Y_AXIS)
|
||||||
{
|
{
|
||||||
@@ -567,11 +565,11 @@ public class MazeDesigner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Collections.shuffle(targets, random);
|
Collections.shuffle(targets, random);
|
||||||
for (IEdge<PartitionNode, DoorwayData> passage : targets)
|
for (IEdge<RoomData, DoorwayData> passage : targets)
|
||||||
{
|
{
|
||||||
if (!components.mergeSets(passage.head(), passage.tail()) && random.nextBoolean())
|
if (!components.mergeSets(passage.head(), passage.tail()) && random.nextBoolean())
|
||||||
{
|
{
|
||||||
rooms.removeEdge(passage);
|
layout.removeEdge(passage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
public class MazeLinkData
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,11 +2,12 @@ package StevenDimDoors.experimental;
|
|||||||
|
|
||||||
import StevenDimDoors.mod_pocketDim.Point3D;
|
import StevenDimDoors.mod_pocketDim.Point3D;
|
||||||
|
|
||||||
public class PartitionNode extends BoundingBox
|
public class PartitionNode<T> extends BoundingBox
|
||||||
{
|
{
|
||||||
private PartitionNode parent;
|
private PartitionNode parent;
|
||||||
private PartitionNode leftChild = null;
|
private PartitionNode leftChild = null;
|
||||||
private PartitionNode rightChild = null;
|
private PartitionNode rightChild = null;
|
||||||
|
private T data = null;
|
||||||
|
|
||||||
public PartitionNode(int width, int height, int length)
|
public PartitionNode(int width, int height, int length)
|
||||||
{
|
{
|
||||||
@@ -122,4 +123,14 @@ public class PartitionNode extends BoundingBox
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setData(T value)
|
||||||
|
{
|
||||||
|
this.data = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData()
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
75
src/main/java/StevenDimDoors/experimental/RoomData.java
Normal file
75
src/main/java/StevenDimDoors/experimental/RoomData.java
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class RoomData
|
||||||
|
{
|
||||||
|
private int distance;
|
||||||
|
private boolean decayed;
|
||||||
|
private PartitionNode partitionNode;
|
||||||
|
private ArrayList<MazeLinkData> inboundLinks;
|
||||||
|
private ArrayList<MazeLinkData> outboundLinks;
|
||||||
|
private IGraphNode<RoomData, DoorwayData> layoutNode;
|
||||||
|
|
||||||
|
public RoomData(PartitionNode partitionNode)
|
||||||
|
{
|
||||||
|
this.partitionNode = partitionNode;
|
||||||
|
this.inboundLinks = new ArrayList<MazeLinkData>();
|
||||||
|
this.outboundLinks = new ArrayList<MazeLinkData>();
|
||||||
|
this.layoutNode = null;
|
||||||
|
this.distance = -1;
|
||||||
|
this.decayed = false;
|
||||||
|
partitionNode.setData(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PartitionNode getPartitionNode()
|
||||||
|
{
|
||||||
|
return this.partitionNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IGraphNode<RoomData, DoorwayData> getLayoutNode()
|
||||||
|
{
|
||||||
|
return this.layoutNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToLayout(DirectedGraph<RoomData, DoorwayData> layout)
|
||||||
|
{
|
||||||
|
this.layoutNode = layout.addNode(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDecayed()
|
||||||
|
{
|
||||||
|
return decayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDecayed(boolean value)
|
||||||
|
{
|
||||||
|
this.decayed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<MazeLinkData> getInboundLinks()
|
||||||
|
{
|
||||||
|
return this.inboundLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<MazeLinkData> getOutboundLinks()
|
||||||
|
{
|
||||||
|
return this.outboundLinks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDistance()
|
||||||
|
{
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDistance(int value)
|
||||||
|
{
|
||||||
|
distance = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
partitionNode = null;
|
||||||
|
layoutNode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user