Partial Implementation of Doors in Mazes
Made progress on implementing the placement of doors in mazes. Still incomplete.
This commit is contained in:
87
src/main/java/StevenDimDoors/experimental/LinkPlan.java
Normal file
87
src/main/java/StevenDimDoors/experimental/LinkPlan.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package StevenDimDoors.experimental;
|
||||
|
||||
public class LinkPlan
|
||||
{
|
||||
private RoomData source;
|
||||
private RoomData destination;
|
||||
private boolean entrance;
|
||||
|
||||
private LinkPlan(RoomData source, RoomData destination, boolean entrance)
|
||||
{
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
this.entrance = entrance;
|
||||
}
|
||||
|
||||
public static LinkPlan createInternalLink(RoomData source, RoomData destination)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new IllegalArgumentException("source cannot be null.");
|
||||
}
|
||||
if (destination == null)
|
||||
{
|
||||
throw new IllegalArgumentException("destination cannot be null.");
|
||||
}
|
||||
LinkPlan plan = new LinkPlan(source, destination, false);
|
||||
source.getOutboundLinks().add(plan);
|
||||
destination.getInboundLinks().add(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
public static LinkPlan createEntranceLink(RoomData source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new IllegalArgumentException("source cannot be null.");
|
||||
}
|
||||
LinkPlan plan = new LinkPlan(source, null, true);
|
||||
source.getOutboundLinks().add(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
public static LinkPlan createDungeonLink(RoomData source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new IllegalArgumentException("source cannot be null.");
|
||||
}
|
||||
LinkPlan plan = new LinkPlan(source, null, false);
|
||||
source.getOutboundLinks().add(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
public RoomData source()
|
||||
{
|
||||
return this.source;
|
||||
}
|
||||
|
||||
public RoomData destination()
|
||||
{
|
||||
return this.destination;
|
||||
}
|
||||
|
||||
public boolean isEntrance()
|
||||
{
|
||||
return entrance;
|
||||
}
|
||||
|
||||
public boolean isInternal()
|
||||
{
|
||||
return (destination != null);
|
||||
}
|
||||
|
||||
public void remove()
|
||||
{
|
||||
if (source != null)
|
||||
{
|
||||
source.getOutboundLinks().remove(this);
|
||||
source = null;
|
||||
}
|
||||
if (destination != null)
|
||||
{
|
||||
destination.getInboundLinks().remove(this);
|
||||
destination = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ public class MazeBuilder
|
||||
|
||||
public static void generate(World world, int x, int y, int z, Random random)
|
||||
{
|
||||
// ISSUE FOR LATER: The room needs to be shifted so as to be centered on its entrance
|
||||
|
||||
MazeDesign design = MazeDesigner.generate(random);
|
||||
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);
|
||||
@@ -21,7 +23,7 @@ public class MazeBuilder
|
||||
buildRooms(design.getLayout(), world, offset);
|
||||
carveDoorways(design.getLayout(), world, offset, decay, random);
|
||||
|
||||
//placeDoors(design, world, offset);
|
||||
placeDoors(design.getLayout(), world, offset);
|
||||
|
||||
applyRandomDestruction(design, world, offset, decay, random);
|
||||
}
|
||||
@@ -41,6 +43,22 @@ public class MazeBuilder
|
||||
}
|
||||
}
|
||||
|
||||
private static void placeDoors(DirectedGraph<RoomData, DoorwayData> layout, World world, Point3D offset)
|
||||
{
|
||||
for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
|
||||
{
|
||||
RoomData room = node.data();
|
||||
Point3D minCorner = room.getPartitionNode().minCorner();
|
||||
if (!room.getOutboundLinks().isEmpty())
|
||||
{
|
||||
setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 1,
|
||||
offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0);
|
||||
setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 2,
|
||||
offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void carveDoorways(DirectedGraph<RoomData, DoorwayData> layout, World world,
|
||||
Point3D offset, SphereDecayOperation decay, Random random)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package StevenDimDoors.experimental;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import java.util.Random;
|
||||
@@ -48,6 +50,9 @@ public class MazeDesigner
|
||||
pruneDoorways(core.getLayoutNode(), layout, random);
|
||||
}
|
||||
|
||||
// Set up the placement of dimensional doors within the maze
|
||||
createMazeLinks(layout, cores, random);
|
||||
|
||||
return new MazeDesign(root, layout);
|
||||
}
|
||||
|
||||
@@ -64,28 +69,6 @@ public class MazeDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeRoom(RoomData room, DirectedGraph<RoomData, DoorwayData> layout)
|
||||
{
|
||||
// Remove the room from the partition tree and from the layout graph.
|
||||
// Also remove any ancestors that become leaf nodes.
|
||||
PartitionNode parent;
|
||||
PartitionNode current;
|
||||
|
||||
current = room.getPartitionNode();
|
||||
while (current != null && current.isLeaf())
|
||||
{
|
||||
parent = current.parent();
|
||||
current.remove();
|
||||
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)
|
||||
{
|
||||
PartitionNode root = new PartitionNode(width, height, length);
|
||||
@@ -468,7 +451,7 @@ public class MazeDesigner
|
||||
// Remove all the rooms that were listed for removal
|
||||
for (RoomData target : removals)
|
||||
{
|
||||
removeRoom(target, layout);
|
||||
target.remove();
|
||||
}
|
||||
return cores;
|
||||
}
|
||||
@@ -529,7 +512,6 @@ public class MazeDesigner
|
||||
// at outbound edges since inbound edges mirror them. List any Y_AXIS
|
||||
// doorways we come across to consider removing them later, depending
|
||||
// on their impact on connectedness.
|
||||
// doorways.
|
||||
ArrayList<IEdge<RoomData, DoorwayData>> targets =
|
||||
new ArrayList<IEdge<RoomData, DoorwayData>>();
|
||||
|
||||
@@ -557,6 +539,7 @@ public class MazeDesigner
|
||||
if (!components.mergeSets(passage.head(), passage.tail()))
|
||||
{
|
||||
layout.removeEdge(passage);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,4 +576,101 @@ public class MazeDesigner
|
||||
}
|
||||
}
|
||||
|
||||
private static void createMazeLinks(DirectedGraph<RoomData, DoorwayData> layout,
|
||||
ArrayList<RoomData> cores, Random random)
|
||||
{
|
||||
// We have 4 objectives here...
|
||||
// 1. Place the entrance to the maze
|
||||
// 2. Place internal links connecting the different sections of the maze
|
||||
// 3. Place links to other dungeons
|
||||
// 4. Place more internal links to confuse people
|
||||
|
||||
// We need to start by counting the door capacity of each section and
|
||||
// listing which rooms can have doors or destinations for each section.
|
||||
int index;
|
||||
int[] capacity = new int[cores.size()];
|
||||
ArrayList<RoomData>[] sourceRooms = (ArrayList<RoomData>[]) Array.newInstance(cores.getClass(), cores.size());
|
||||
ArrayList<RoomData>[] destinationRooms = (ArrayList<RoomData>[]) Array.newInstance(cores.getClass(), cores.size());
|
||||
|
||||
for (index = 0; index < sourceRooms.length; index++)
|
||||
{
|
||||
sourceRooms[index] = new ArrayList<RoomData>();
|
||||
destinationRooms[index] = new ArrayList<RoomData>();
|
||||
capacity[index] = listLinkRooms(cores.get(index).getLayoutNode(), sourceRooms[index], destinationRooms[index]);
|
||||
}
|
||||
|
||||
// Now we select the room in which to place the entrance.
|
||||
// We can safely assume all source room lists are non-empty because
|
||||
// createMazeSections() guarantees that each section has at least
|
||||
// the capacity for 2 doors.
|
||||
index = random.nextInt(sourceRooms.length);
|
||||
createEntranceLink(sourceRooms[index], random.nextInt(sourceRooms[index].size()));
|
||||
|
||||
// The next task is to place internal links. These links must connect
|
||||
// the different maze sections to create a strongly connected graph.
|
||||
|
||||
}
|
||||
|
||||
private static int listLinkRooms(IGraphNode<RoomData, DoorwayData> core,
|
||||
ArrayList<RoomData> sourceRooms, ArrayList<RoomData> destinationRooms)
|
||||
{
|
||||
int capacity = 0;
|
||||
boolean hasHoles;
|
||||
RoomData currentRoom;
|
||||
IGraphNode<RoomData, DoorwayData> current;
|
||||
IGraphNode<RoomData, DoorwayData> neighbor;
|
||||
Stack<IGraphNode<RoomData, DoorwayData>> ordering = new Stack<IGraphNode<RoomData, DoorwayData>>();
|
||||
HashSet<IGraphNode<RoomData, DoorwayData>> visited = new HashSet<IGraphNode<RoomData, DoorwayData>>();
|
||||
|
||||
visited.add(core);
|
||||
ordering.add(core);
|
||||
while (!ordering.isEmpty())
|
||||
{
|
||||
current = ordering.pop();
|
||||
hasHoles = false;
|
||||
|
||||
for (IEdge<RoomData, DoorwayData> edge : current.outbound())
|
||||
{
|
||||
neighbor = edge.tail();
|
||||
if (visited.add(neighbor))
|
||||
{
|
||||
ordering.add(neighbor);
|
||||
}
|
||||
}
|
||||
for (IEdge<RoomData, DoorwayData> edge : current.inbound())
|
||||
{
|
||||
neighbor = edge.head();
|
||||
if (visited.add(neighbor))
|
||||
{
|
||||
ordering.add(neighbor);
|
||||
}
|
||||
if (edge.data().axis() == DoorwayData.Y_AXIS)
|
||||
{
|
||||
hasHoles = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasHoles)
|
||||
{
|
||||
currentRoom = current.data();
|
||||
destinationRooms.add(currentRoom);
|
||||
if (currentRoom.estimateDoorCapacity() > 0)
|
||||
{
|
||||
capacity += currentRoom.estimateDoorCapacity();
|
||||
sourceRooms.add(currentRoom);
|
||||
}
|
||||
}
|
||||
}
|
||||
return capacity;
|
||||
}
|
||||
|
||||
private static void createEntranceLink(ArrayList<RoomData> sources, int index)
|
||||
{
|
||||
RoomData entranceRoom = sources.get(index);
|
||||
LinkPlan.createEntranceLink(entranceRoom);
|
||||
if (entranceRoom.getRemainingDoorCapacity() == 0)
|
||||
{
|
||||
sources.remove(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
package StevenDimDoors.experimental;
|
||||
|
||||
public class MazeLinkData
|
||||
{
|
||||
|
||||
}
|
||||
@@ -4,20 +4,32 @@ import java.util.ArrayList;
|
||||
|
||||
public class RoomData
|
||||
{
|
||||
/* Implementation Note:
|
||||
* Plans for links between rooms are stored in lists rather than a graph,
|
||||
* even though they duplicate some graph functionality, because there are
|
||||
* relatively few of them compared to the number of rooms. Moreover, some
|
||||
* links don't even have destinations because they're intended to lead to
|
||||
* other dungeons.
|
||||
*/
|
||||
|
||||
private int capacity;
|
||||
private int distance;
|
||||
private boolean decayed;
|
||||
private PartitionNode partitionNode;
|
||||
private ArrayList<MazeLinkData> inboundLinks;
|
||||
private ArrayList<MazeLinkData> outboundLinks;
|
||||
private ArrayList<LinkPlan> inboundLinks;
|
||||
private ArrayList<LinkPlan> outboundLinks;
|
||||
private DirectedGraph<RoomData, DoorwayData> layout;
|
||||
private IGraphNode<RoomData, DoorwayData> layoutNode;
|
||||
|
||||
public RoomData(PartitionNode partitionNode)
|
||||
{
|
||||
this.partitionNode = partitionNode;
|
||||
this.inboundLinks = new ArrayList<MazeLinkData>();
|
||||
this.outboundLinks = new ArrayList<MazeLinkData>();
|
||||
this.inboundLinks = new ArrayList<LinkPlan>();
|
||||
this.outboundLinks = new ArrayList<LinkPlan>();
|
||||
this.layoutNode = null;
|
||||
this.layout = null;
|
||||
this.distance = -1;
|
||||
this.capacity = -1;
|
||||
this.decayed = false;
|
||||
partitionNode.setData(this);
|
||||
}
|
||||
@@ -34,6 +46,7 @@ public class RoomData
|
||||
|
||||
public void addToLayout(DirectedGraph<RoomData, DoorwayData> layout)
|
||||
{
|
||||
this.layout = layout;
|
||||
this.layoutNode = layout.addNode(this);
|
||||
}
|
||||
|
||||
@@ -47,12 +60,12 @@ public class RoomData
|
||||
this.decayed = value;
|
||||
}
|
||||
|
||||
public ArrayList<MazeLinkData> getInboundLinks()
|
||||
public ArrayList<LinkPlan> getInboundLinks()
|
||||
{
|
||||
return this.inboundLinks;
|
||||
}
|
||||
|
||||
public ArrayList<MazeLinkData> getOutboundLinks()
|
||||
public ArrayList<LinkPlan> getOutboundLinks()
|
||||
{
|
||||
return this.outboundLinks;
|
||||
}
|
||||
@@ -67,16 +80,54 @@ public class RoomData
|
||||
distance = value;
|
||||
}
|
||||
|
||||
public void clear()
|
||||
public void remove()
|
||||
{
|
||||
// Remove the room from the partition tree and from the layout graph.
|
||||
// Also remove any ancestors that become leaf nodes.
|
||||
PartitionNode parent;
|
||||
PartitionNode current = partitionNode;
|
||||
while (current != null && current.isLeaf())
|
||||
{
|
||||
parent = current.parent();
|
||||
current.remove();
|
||||
current = parent;
|
||||
}
|
||||
|
||||
// Remove the room from the layout graph
|
||||
layout.removeNode(layoutNode);
|
||||
|
||||
// Remove any links
|
||||
while (!inboundLinks.isEmpty())
|
||||
inboundLinks.get(inboundLinks.size() - 1).remove();
|
||||
|
||||
while (!outboundLinks.isEmpty())
|
||||
outboundLinks.get(outboundLinks.size() - 1).remove();
|
||||
|
||||
// Wipe the room's data, as a precaution
|
||||
layout = null;
|
||||
partitionNode = null;
|
||||
layoutNode = null;
|
||||
inboundLinks = null;
|
||||
outboundLinks = null;
|
||||
}
|
||||
|
||||
public int estimateDoorCapacity()
|
||||
{
|
||||
if (capacity >= 0)
|
||||
return capacity;
|
||||
|
||||
int cellsX = (partitionNode.width() - 3) / 2;
|
||||
int cellsZ = (partitionNode.length() - 3) / 2;
|
||||
return Math.min(cellsX * cellsZ, 3);
|
||||
capacity = Math.min(cellsX * cellsZ, 3);
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public int getRemainingDoorCapacity()
|
||||
{
|
||||
return (estimateDoorCapacity() - outboundLinks.size());
|
||||
}
|
||||
|
||||
public boolean isProtected()
|
||||
{
|
||||
return !inboundLinks.isEmpty() || !outboundLinks.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user