Progress on Maze Generation

* Finished implementing link planning for mazes. Doors aren't placed yet
because that's up to Decorators and those haven't been implemented yet.
* Added bounding walls to mazes.
* Added decay effects to mazes.
This commit is contained in:
SenseiKiwi
2014-04-14 22:24:59 -04:00
parent 906faf44eb
commit 81b48158bd
7 changed files with 544 additions and 122 deletions

View File

@@ -1,52 +1,47 @@
package StevenDimDoors.experimental;
import StevenDimDoors.mod_pocketDim.Point3D;
public class LinkPlan
{
private RoomData source;
private RoomData destination;
private boolean entrance;
private Point3D sourcePoint;
private Point3D destinationPoint;
private final boolean entrance;
private final boolean internal;
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)
private LinkPlan(RoomData source, boolean entrance, boolean internal)
{
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);
this.source = source;
this.destination = null;
this.sourcePoint = null;
this.destinationPoint = null;
this.entrance = entrance;
this.internal = internal;
}
public static LinkPlan createInternalLink(RoomData source)
{
LinkPlan plan = new LinkPlan(source, false, true);
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);
LinkPlan plan = new LinkPlan(source, true, false);
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);
LinkPlan plan = new LinkPlan(source, false, false);
source.getOutboundLinks().add(plan);
return plan;
}
@@ -68,7 +63,7 @@ public class LinkPlan
public boolean isInternal()
{
return (destination != null);
return internal;
}
public void remove()
@@ -84,4 +79,42 @@ public class LinkPlan
destination = null;
}
}
public void setDestination(RoomData destination)
{
if (!internal)
{
throw new IllegalStateException("LinkPlan.setDestination() is only applicable to internal links.");
}
if (this.destination != null)
{
throw new IllegalStateException("destination can only be set once.");
}
if (destination == null)
{
throw new IllegalArgumentException("destination cannot be null.");
}
this.destination = destination;
destination.getInboundLinks().add(this);
}
public Point3D sourcePoint()
{
return this.sourcePoint;
}
public Point3D destinationPoint()
{
return this.destinationPoint;
}
public void setSourcePoint(Point3D value)
{
this.sourcePoint = value;
}
public void setDestinationPoint(Point3D value)
{
this.destinationPoint = value;
}
}

View File

@@ -1,18 +1,23 @@
package StevenDimDoors.experimental;
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;
import net.minecraft.block.Block;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import StevenDimDoors.mod_pocketDim.Point3D;
import StevenDimDoors.mod_pocketDim.config.DDProperties;
public class MazeBuilder
{
private static final int POCKET_WALL_GAP = 4;
private MazeBuilder() { }
public static void generate(World world, int x, int y, int z, Random random)
public static void generate(World world, int x, int y, int z, Random random, DDProperties properties)
{
// ISSUE FOR LATER: The room needs to be shifted so as to be centered on its entrance
@@ -22,16 +27,85 @@ public class MazeBuilder
buildRooms(design.getLayout(), world, offset);
carveDoorways(design.getLayout(), world, offset, decay, random);
placeDoors(design.getLayout(), world, offset);
applyRandomDestruction(design, world, offset, decay, random);
decorateRooms(design.getLayout(), world, offset);
buildPocketWalls(design, world, offset, properties);
}
private static void applyRandomDestruction(MazeDesign design, World world,
Point3D offset, SphereDecayOperation decay, Random random)
{
//final int DECAY_BOX_SIZE = 8
final int DECAY_BOX_SIZE = 7;
final int DECAY_OPERATIONS = 5 + random.nextInt(5);
final int DECAY_ATTEMPTS = 20;
int x, y, z;
int successes = 0;
int attempts = 0;
PartitionNode root = design.getRootPartition();
for (; successes < DECAY_OPERATIONS && attempts < DECAY_ATTEMPTS; attempts++)
{
// Select the coordinates at which to apply the decay operation
x = random.nextInt(design.width()) - DECAY_BOX_SIZE / 2;
y = random.nextInt(design.height()) - DECAY_BOX_SIZE / 2;
z = random.nextInt(design.length()) - DECAY_BOX_SIZE / 2;
// Check that the decay operation would not impact any protected areas
// and mark the affected areas as decayed
if (markDecayArea(x, y, z, DECAY_BOX_SIZE, root))
{
// Apply decay
decay.apply(world, offset.getX() + x, offset.getY() + y, offset.getZ() + z,
DECAY_BOX_SIZE, DECAY_BOX_SIZE, DECAY_BOX_SIZE);
successes++;
}
}
}
private static boolean markDecayArea(int x, int y, int z, int DECAY_BOX_SIZE, PartitionNode<RoomData> root)
{
// Check if a given PartitionNode intersects the decay area. If it's a leaf, then check
// if it's protected or not. Otherwise, check its children. The specific area is valid
// if and only if there are no protected rooms and at least one (unprotected) room in it.
// Also list the unprotected rooms to mark them if the decay operation will proceed.
RoomData room;
PartitionNode<RoomData> partition;
ArrayList<RoomData> targets = new ArrayList<RoomData>();
Stack<PartitionNode<RoomData>> nodes = new Stack<PartitionNode<RoomData>>();
BoundingBox decayBounds = new BoundingBox(x, y, z, DECAY_BOX_SIZE, DECAY_BOX_SIZE, DECAY_BOX_SIZE);
// Use depth-first search to explore all intersecting partitions
nodes.push(root);
while (!nodes.isEmpty())
{
partition = nodes.pop();
if (decayBounds.intersects(partition))
{
if (partition.isLeaf())
{
room = partition.getData();
if (room.isProtected())
return false;
targets.add(room);
}
else
{
if (partition.leftChild() != null)
nodes.push(partition.leftChild());
if (partition.rightChild() != null)
nodes.push(partition.rightChild());
}
}
}
// If execution has reached this point, then there were no protected rooms.
// Mark all intersecting rooms as decayed.
for (RoomData target : targets)
{
target.setDecayed(true);
}
return !targets.isEmpty();
}
private static void buildRooms(DirectedGraph<RoomData, DoorwayData> layout, World world, Point3D offset)
@@ -43,19 +117,25 @@ public class MazeBuilder
}
}
private static void placeDoors(DirectedGraph<RoomData, DoorwayData> layout, World world, Point3D offset)
private static void decorateRooms(DirectedGraph<RoomData, DoorwayData> layout, World world, Point3D offset)
{
RoomData room;
PartitionNode<RoomData> partition;
ArrayList<LinkPlan> links = new ArrayList<LinkPlan>();
// Iterate over all rooms and apply decorators
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);
}
room = node.data();
partition = room.getPartitionNode();
links.addAll(room.getOutboundLinks());
// TODO: Add decorator code here!
}
// Iterate over all links plans and place links in the world
for (LinkPlan link : links)
{
// TODO: Add link placement code here!
}
}
@@ -70,12 +150,19 @@ public class MazeBuilder
{
for (IEdge<RoomData, DoorwayData> passage : node.outbound())
{
// Carve out the passage
doorway = passage.data();
axis = doorway.axis();
lower = doorway.minCorner();
carveDoorway(world, axis, offset.getX() + lower.getX(), offset.getY() + lower.getY(),
offset.getZ() + lower.getZ(), doorway.width(), doorway.height(), doorway.length(),
decay, random);
// If this is a vertical passage, then mark the upper room as decayed
if (axis == DoorwayData.Y_AXIS)
{
passage.tail().data().setDecayed(true);
}
}
}
}
@@ -86,6 +173,8 @@ public class MazeBuilder
final int MIN_DOUBLE_DOOR_SPAN = 10;
int gap;
int rx;
int rz;
switch (axis)
{
case DoorwayData.X_AXIS:
@@ -150,9 +239,10 @@ public class MazeBuilder
{
gap = 6;
}
decay.apply(world,
x + random.nextInt(width - gap - 1) + 1, y - 1,
z + random.nextInt(length - gap - 1) + 1, gap, 4, gap);
rx = x + random.nextInt(width - gap - 1) + 1;
rz = z + random.nextInt(length - gap - 1) + 1;
carveHole(world, rx + gap / 2, y, rz + gap / 2);
decay.apply(world, rx, y - 1, rz, gap, 4, gap);
}
else
{
@@ -184,6 +274,19 @@ public class MazeBuilder
setBlockDirectly(world, x, y + 1, z, 0, 0);
}
private static void buildPocketWalls(MazeDesign design, World world, Point3D offset, DDProperties properties)
{
// Build the inner Fabric of Reality box
Point3D minCorner = new Point3D(-POCKET_WALL_GAP - 1, -POCKET_WALL_GAP - 1, -POCKET_WALL_GAP - 1);
Point3D maxCorner = new Point3D(design.width() + POCKET_WALL_GAP, design.height() + POCKET_WALL_GAP, design.length() + POCKET_WALL_GAP);
buildBox(world, offset, minCorner, maxCorner, properties.FabricBlockID, 0);
// Build the outer Eternal Fabric box
minCorner.add(-1, -1, -1);
maxCorner.add(1, 1, 1);
buildBox(world, offset, minCorner, maxCorner, properties.PermaFabricBlockID, 0);
}
private static void buildBox(World world, Point3D offset, Point3D minCorner, Point3D maxCorner, int blockID, int metadata)
{
int minX = minCorner.getX() + offset.getX();

View File

@@ -4,16 +4,16 @@ import java.util.ArrayList;
public class MazeDesign
{
private PartitionNode root;
private PartitionNode<RoomData> root;
private DirectedGraph<RoomData, DoorwayData> layout;
public MazeDesign(PartitionNode root, DirectedGraph<RoomData, DoorwayData> layout)
public MazeDesign(PartitionNode<RoomData> root, DirectedGraph<RoomData, DoorwayData> layout)
{
this.root = root;
this.layout = layout;
}
public PartitionNode getRootPartition()
public PartitionNode<RoomData> getRootPartition()
{
return root;
}

View File

@@ -589,93 +589,180 @@ public class MazeDesigner
// 3. Place internal links connecting the different sections of the maze
// 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.
// We need to start by building up data for each section, such as their
// door capacities and the rooms available for placing doors.
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());
int count;
SectionData selection;
SectionData destination;
ArrayList<SectionData> allSections;
ArrayList<SectionData> usableSections;
for (index = 0; index < sourceRooms.length; index++)
// Check if there is only one section. Our concerns differ depending
// on whether there is one or more than one.
if (cores.size() > 1)
{
sourceRooms[index] = new ArrayList<RoomData>();
destinationRooms[index] = new ArrayList<RoomData>();
capacity[index] = listLinkRooms(cores.get(index).getLayoutNode(), sourceRooms[index], destinationRooms[index]);
// More than 1 section
allSections = new ArrayList<SectionData>(cores.size());
for (RoomData core : cores)
{
allSections.add( SectionData.createFromCore(core.getLayoutNode()) );
}
usableSections = (ArrayList<SectionData>) allSections.clone();
// Select the room in which to place the entrance.
// We can safely consider all sections because createMazeSections()
// guarantees that each one has at least the capacity for 2 doors.
// Remove the selected section if it falls below a capacity of 2
// since we need to leave at least 1 capacity for section linking.
index = random.nextInt(usableSections.size());
selection = usableSections.get(index);
selection.createEntranceLink(random);
if (selection.capacity() <= 1)
{
usableSections.remove(index);
}
// Place 3 to 4 dungeon doors in random sections
// Remove any sections that fall under a capacity of 2.
count = 3 + random.nextInt(2);
for (; count > 0 && !usableSections.isEmpty(); count--)
{
index = random.nextInt(usableSections.size());
selection = usableSections.get(index);
selection.createDungeonLink(random);
if (selection.capacity() <= 1)
{
usableSections.remove(index);
}
}
// The next task is to place internal links. These links must connect
// the different maze sections to create a strongly connected graph.
linkMazeSections(allSections, random);
// Add 1 to 3 extra internal links to confuse people
usableSections.clear();
for (SectionData section : allSections)
{
if (section.capacity() > 0)
{
usableSections.add(section);
}
}
count = 1 + random.nextInt(3);
for (; count > 0 && !usableSections.isEmpty(); count--)
{
index = random.nextInt(usableSections.size());
selection = usableSections.get(index);
destination = allSections.get( random.nextInt(allSections.size()) );
selection.reserveSectionLink(destination, random);
if (selection.capacity() == 0)
{
usableSections.remove(index);
}
}
// Finally, make sure to process all reservations for section links.
for (SectionData section : allSections)
{
section.processReservedLinks(random);
}
}
else
{
// Only 1 section
selection = SectionData.createFromCore(cores.get(0).getLayoutNode());
// Place entrance door in a random room
selection.createEntranceLink(random);
// Place 3 to 4 dungeon doors or fewer, based on capacity
count = Math.min(3 + random.nextInt(2), selection.capacity());
for (; count > 0; count--)
{
selection.createDungeonLink(random);
}
}
// 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)
private static void linkMazeSections(ArrayList<SectionData> sections, Random random)
{
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>>();
// This algorithm constructs links sections together using Dimensional Doors
// to create a random strongly connected graph. It takes into account capacity
// constraints as well. We assume that all sections have at least 1 door capacity.
final int EXTENSION_CHANCE = 2;
final int MAX_EXTENSION_CHANCE = 3;
visited.add(core);
ordering.add(core);
while (!ordering.isEmpty())
int index;
SectionData start;
SectionData current;
SectionData next;
// Total spare capacity of the sections not in "remaining"
int capacity;
// Sections not in the graph
ArrayList<SectionData> remaining = (ArrayList<SectionData>) sections.clone();
// Sections that are part of an incomplete cycle
ArrayList<SectionData> attached = new ArrayList<SectionData>(sections.size());
// Sections that are part of a cycle and thus strongly connected
ArrayList<SectionData> connected = new ArrayList<SectionData>(sections.size());
// Sections that are part of a cycle and have spare capacity - used to start new cycles
ArrayList<SectionData> starters = new ArrayList<SectionData>(sections.size());
// Shuffle remaining to achieve randomness
Collections.shuffle(remaining, random);
// Remove the starting node to serve as the base of our strongly connected graph
start = remaining.remove(remaining.size() - 1);
starters.add(start);
connected.add(start);
capacity = start.capacity();
// Repeat until all sections are connected
while (!remaining.isEmpty())
{
current = ordering.pop();
hasHoles = false;
for (IEdge<RoomData, DoorwayData> edge : current.outbound())
// Select a section from which to start a new cycle
index = random.nextInt(starters.size());
start = starters.get(index);
// Select the first new section in the cycle and link to it
current = remaining.remove(remaining.size() - 1);
attached.add(current);
start.reserveSectionLink(current, random);
// Add the current section's capacity to the total, but subtract two to account
// for the link just created and for the future link from this section to another
capacity += current.capacity() - 2;
// Remove the starting section from starters if it has exhausted its capacity
if (start.capacity() == 0)
{
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;
}
starters.remove(index);
}
if (!hasHoles)
// Continue attaching sections to the partial cycle while there are are still sections
// left to be added and we have no spare capacity. Or we could randomly decide to
// continue even with spare capacity. Spare capacity means we could start a new cycle
// safely and still achieve strong connectivity. Randomness here influences the kinds
// of graphs we can get.
while (!remaining.isEmpty() && (capacity == 0 ||
random.nextInt(MAX_EXTENSION_CHANCE) < EXTENSION_CHANCE))
{
currentRoom = current.data();
destinationRooms.add(currentRoom);
if (currentRoom.estimateDoorCapacity() > 0)
next = remaining.remove(remaining.size() - 1);
attached.add(next);
current.reserveSectionLink(next, random);
// Account for this section's capacity, but subtract one for
// the future link that will connect this section to another
capacity += next.capacity() - 1;
current = next;
}
next = connected.get(random.nextInt(connected.size()));
current.reserveSectionLink(next, random);
for (SectionData section : attached)
{
connected.add(section);
if (section.capacity() > 0)
{
capacity += currentRoom.estimateDoorCapacity();
sourceRooms.add(currentRoom);
starters.add(section);
}
}
}
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);
}
// Done! At this point, all sections are connected.
}
}

View File

@@ -0,0 +1,192 @@
package StevenDimDoors.experimental;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Random;
import java.util.Stack;
public class SectionData
{
// Specifies the chance of selecting a destination from protectedRooms
// rather than from destinationRooms (which will then become protected)
private static final int PROTECTED_DESTINATION_CHANCE = 4;
private static final int MAX_PROTECTED_DESTINATION_CHANCE = 5;
private int capacity;
private ArrayList<RoomData> sourceRooms;
private ArrayList<RoomData> protectedRooms;
private ArrayList<RoomData> destinationRooms;
private ArrayList<LinkPlan> reservations;
private SectionData(ArrayList<RoomData> sourceRooms, ArrayList<RoomData> destinationRooms, int capacity)
{
this.capacity = capacity;
this.sourceRooms = sourceRooms;
this.destinationRooms = destinationRooms;
this.protectedRooms = new ArrayList<RoomData>();
this.reservations = new ArrayList<LinkPlan>();
}
public static SectionData createFromCore(IGraphNode<RoomData, DoorwayData> core)
{
int capacity = 0;
ArrayList<RoomData> sourceRooms = new ArrayList<RoomData>();
ArrayList<RoomData> destinationRooms = new ArrayList<RoomData>();
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 new SectionData(sourceRooms, destinationRooms, capacity);
}
public int capacity()
{
return capacity;
}
public void createEntranceLink(Random random)
{
int index = random.nextInt(sourceRooms.size());
RoomData room = sourceRooms.get(index);
LinkPlan.createEntranceLink(room);
if (room.getRemainingDoorCapacity() == 0)
{
sourceRooms.remove(index);
}
// It's okay to check containment in this list because
// the number of protected rooms is expected to be small
if (!protectedRooms.contains(room))
{
protectedRooms.add(room);
}
capacity--;
}
public void createDungeonLink(Random random)
{
int index = random.nextInt(sourceRooms.size());
RoomData room = sourceRooms.get(index);
LinkPlan.createDungeonLink(room);
if (room.getRemainingDoorCapacity() == 0)
{
sourceRooms.remove(index);
}
// It's okay to check containment in this list because
// the number of protected rooms is expected to be small
if (!protectedRooms.contains(room))
{
protectedRooms.add(room);
}
capacity--;
}
public void reserveSectionLink(SectionData destination, Random random)
{
// This method "reserves" a link by decrementing the capacity of this
// section and assigning a source room to the link. However, assigning
// its destination in a particular section is deferred. Why?
// We favor using source rooms as destinations to cut down the number
// of rooms that have to be marked as protected against decay effects.
// We defer assigning a destination until after all source rooms are
// known so that we have that information available. Otherwise,
// destination selection would be biased toward non-source rooms and
// rooms with dungeon doors, which are placed before section links.
int index = random.nextInt(sourceRooms.size());
RoomData room = sourceRooms.get(index);
destination.reserveDestination(LinkPlan.createInternalLink(room));
if (room.getRemainingDoorCapacity() == 0)
{
sourceRooms.remove(index);
}
// It's okay to check containment in this list because
// the number of protected rooms is expected to be small
if (!protectedRooms.contains(room))
{
protectedRooms.add(room);
}
capacity--;
}
public void processReservedLinks(Random random)
{
for (LinkPlan link : reservations)
{
link.setDestination( getLinkDestination(random) );
}
reservations.clear();
}
private void reserveDestination(LinkPlan link)
{
reservations.add(link);
}
private RoomData getLinkDestination(Random random)
{
RoomData destination;
// Choose whether to select a room that is already protected or select
// from all possible destination rooms. Note that some destination rooms
// may also be protected rooms already.
if (random.nextInt(MAX_PROTECTED_DESTINATION_CHANCE) < PROTECTED_DESTINATION_CHANCE)
{
destination = protectedRooms.get( random.nextInt(protectedRooms.size()) );
}
else
{
destination = destinationRooms.get( random.nextInt(destinationRooms.size()) );
// It's okay to check containment in this list because
// the number of protected rooms is expected to be small
if (!protectedRooms.contains(destination))
{
protectedRooms.add(destination);
}
}
return destination;
}
}

View File

@@ -56,6 +56,13 @@ public class Point3D implements Serializable {
return this.z = z;
}
public void add(int x, int y, int z)
{
this.x += x;
this.y += y;
this.z += z;
}
@Override
public Point3D clone()
{

View File

@@ -477,7 +477,7 @@ public class PocketBuilder
}
*/
MazeBuilder.generate(world, x, y, z, random);
MazeBuilder.generate(world, x, y, z, random, properties);
//Build the door
int doorOrientation = BlockRotator.transformMetadata(BlockRotator.EAST_DOOR_METADATA, orientation - BlockRotator.EAST_DOOR_METADATA + 2, properties.DimensionalDoorID);