Mazes #155

Merged
StevenRS11 merged 11 commits from mazes into mazes 2014-04-15 11:46:00 +00:00
7 changed files with 544 additions and 122 deletions
Showing only changes of commit 81b48158bd - Show all commits

View File

@@ -1,52 +1,47 @@
package StevenDimDoors.experimental; package StevenDimDoors.experimental;
import StevenDimDoors.mod_pocketDim.Point3D;
public class LinkPlan public class LinkPlan
{ {
private RoomData source; private RoomData source;
private RoomData destination; 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) private LinkPlan(RoomData source, boolean entrance, boolean internal)
{
this.source = source;
this.destination = destination;
this.entrance = entrance;
}
public static LinkPlan createInternalLink(RoomData source, RoomData destination)
{ {
if (source == null) if (source == null)
{ {
throw new IllegalArgumentException("source cannot be null."); throw new IllegalArgumentException("source cannot be null.");
} }
if (destination == null) this.source = source;
{ this.destination = null;
throw new IllegalArgumentException("destination cannot be null."); this.sourcePoint = null;
this.destinationPoint = null;
this.entrance = entrance;
this.internal = internal;
} }
LinkPlan plan = new LinkPlan(source, destination, false);
public static LinkPlan createInternalLink(RoomData source)
{
LinkPlan plan = new LinkPlan(source, false, true);
source.getOutboundLinks().add(plan); source.getOutboundLinks().add(plan);
destination.getInboundLinks().add(plan);
return plan; return plan;
} }
public static LinkPlan createEntranceLink(RoomData source) public static LinkPlan createEntranceLink(RoomData source)
{ {
if (source == null) LinkPlan plan = new LinkPlan(source, true, false);
{
throw new IllegalArgumentException("source cannot be null.");
}
LinkPlan plan = new LinkPlan(source, null, true);
source.getOutboundLinks().add(plan); source.getOutboundLinks().add(plan);
return plan; return plan;
} }
public static LinkPlan createDungeonLink(RoomData source) public static LinkPlan createDungeonLink(RoomData source)
{ {
if (source == null) LinkPlan plan = new LinkPlan(source, false, false);
{
throw new IllegalArgumentException("source cannot be null.");
}
LinkPlan plan = new LinkPlan(source, null, false);
source.getOutboundLinks().add(plan); source.getOutboundLinks().add(plan);
return plan; return plan;
} }
@@ -68,7 +63,7 @@ public class LinkPlan
public boolean isInternal() public boolean isInternal()
{ {
return (destination != null); return internal;
} }
public void remove() public void remove()
@@ -84,4 +79,42 @@ public class LinkPlan
destination = null; 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; package StevenDimDoors.experimental;
import java.util.ArrayList;
import java.util.Random; import java.util.Random;
import java.util.Stack;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.Point3D;
import StevenDimDoors.mod_pocketDim.config.DDProperties;
public class MazeBuilder public class MazeBuilder
{ {
private static final int POCKET_WALL_GAP = 4;
private MazeBuilder() { } 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 // 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); buildRooms(design.getLayout(), world, offset);
carveDoorways(design.getLayout(), world, offset, decay, random); carveDoorways(design.getLayout(), world, offset, decay, random);
placeDoors(design.getLayout(), world, offset);
applyRandomDestruction(design, world, offset, decay, random); applyRandomDestruction(design, world, offset, decay, random);
decorateRooms(design.getLayout(), world, offset);
buildPocketWalls(design, world, offset, properties);
} }
private static void applyRandomDestruction(MazeDesign design, World world, private static void applyRandomDestruction(MazeDesign design, World world,
Point3D offset, SphereDecayOperation decay, Random random) 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) 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()) for (IGraphNode<RoomData, DoorwayData> node : layout.nodes())
{ {
RoomData room = node.data(); room = node.data();
Point3D minCorner = room.getPartitionNode().minCorner(); partition = room.getPartitionNode();
if (!room.getOutboundLinks().isEmpty()) links.addAll(room.getOutboundLinks());
{
setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 1, // TODO: Add decorator code here!
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);
} }
// 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()) for (IEdge<RoomData, DoorwayData> passage : node.outbound())
{ {
// Carve out the passage
doorway = passage.data(); doorway = passage.data();
axis = doorway.axis(); axis = doorway.axis();
lower = doorway.minCorner(); lower = doorway.minCorner();
carveDoorway(world, axis, offset.getX() + lower.getX(), offset.getY() + lower.getY(), carveDoorway(world, axis, offset.getX() + lower.getX(), offset.getY() + lower.getY(),
offset.getZ() + lower.getZ(), doorway.width(), doorway.height(), doorway.length(), offset.getZ() + lower.getZ(), doorway.width(), doorway.height(), doorway.length(),
decay, random); 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; final int MIN_DOUBLE_DOOR_SPAN = 10;
int gap; int gap;
int rx;
int rz;
switch (axis) switch (axis)
{ {
case DoorwayData.X_AXIS: case DoorwayData.X_AXIS:
@@ -150,9 +239,10 @@ public class MazeBuilder
{ {
gap = 6; gap = 6;
} }
decay.apply(world, rx = x + random.nextInt(width - gap - 1) + 1;
x + random.nextInt(width - gap - 1) + 1, y - 1, rz = z + random.nextInt(length - gap - 1) + 1;
z + random.nextInt(length - gap - 1) + 1, gap, 4, gap); carveHole(world, rx + gap / 2, y, rz + gap / 2);
decay.apply(world, rx, y - 1, rz, gap, 4, gap);
} }
else else
{ {
@@ -184,6 +274,19 @@ public class MazeBuilder
setBlockDirectly(world, x, y + 1, z, 0, 0); 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) 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();

View File

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

View File

@@ -589,93 +589,180 @@ public class MazeDesigner
// 3. Place internal links connecting the different sections of the maze // 3. Place internal links connecting the different sections of the maze
// 4. Place more internal links to confuse people // 4. Place more internal links to confuse people
// We need to start by counting the door capacity of each section and // We need to start by building up data for each section, such as their
// listing which rooms can have doors or destinations for each section. // door capacities and the rooms available for placing doors.
int index; int index;
int[] capacity = new int[cores.size()]; int count;
ArrayList<RoomData>[] sourceRooms = (ArrayList<RoomData>[]) Array.newInstance(cores.getClass(), cores.size()); SectionData selection;
ArrayList<RoomData>[] destinationRooms = (ArrayList<RoomData>[]) Array.newInstance(cores.getClass(), cores.size()); 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>(); // More than 1 section
destinationRooms[index] = new ArrayList<RoomData>(); allSections = new ArrayList<SectionData>(cores.size());
capacity[index] = listLinkRooms(cores.get(index).getLayoutNode(), sourceRooms[index], destinationRooms[index]); 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);
} }
// Now we select the room in which to place the entrance. // Place 3 to 4 dungeon doors in random sections
// We can safely assume all source room lists are non-empty because // Remove any sections that fall under a capacity of 2.
// createMazeSections() guarantees that each section has at least count = 3 + random.nextInt(2);
// the capacity for 2 doors. for (; count > 0 && !usableSections.isEmpty(); count--)
index = random.nextInt(sourceRooms.length); {
createEntranceLink(sourceRooms[index], random.nextInt(sourceRooms[index].size())); 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 next task is to place internal links. These links must connect
// the different maze sections to create a strongly connected graph. // 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)
private static int listLinkRooms(IGraphNode<RoomData, DoorwayData> core,
ArrayList<RoomData> sourceRooms, ArrayList<RoomData> destinationRooms)
{ {
int capacity = 0; if (section.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(); usableSections.add(section);
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()) count = 1 + random.nextInt(3);
for (; count > 0 && !usableSections.isEmpty(); count--)
{ {
neighbor = edge.head(); index = random.nextInt(usableSections.size());
if (visited.add(neighbor)) selection = usableSections.get(index);
destination = allSections.get( random.nextInt(allSections.size()) );
selection.reserveSectionLink(destination, random);
if (selection.capacity() == 0)
{ {
ordering.add(neighbor); usableSections.remove(index);
}
if (edge.data().axis() == DoorwayData.Y_AXIS)
{
hasHoles = true;
} }
} }
if (!hasHoles) // Finally, make sure to process all reservations for section links.
for (SectionData section : allSections)
{ {
currentRoom = current.data(); section.processReservedLinks(random);
destinationRooms.add(currentRoom); }
if (currentRoom.estimateDoorCapacity() > 0) }
else
{ {
capacity += currentRoom.estimateDoorCapacity(); // Only 1 section
sourceRooms.add(currentRoom); 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);
} }
} }
} }
return capacity;
}
private static void createEntranceLink(ArrayList<RoomData> sources, int index) private static void linkMazeSections(ArrayList<SectionData> sections, Random random)
{ {
RoomData entranceRoom = sources.get(index); // This algorithm constructs links sections together using Dimensional Doors
LinkPlan.createEntranceLink(entranceRoom); // to create a random strongly connected graph. It takes into account capacity
if (entranceRoom.getRemainingDoorCapacity() == 0) // 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;
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())
{ {
sources.remove(index); // 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)
{
starters.remove(index);
}
// 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))
{
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)
{
starters.add(section);
} }
} }
} }
// 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; return this.z = z;
} }
public void add(int x, int y, int z)
{
this.x += x;
this.y += y;
this.z += z;
}
@Override @Override
public Point3D clone() 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 //Build the door
int doorOrientation = BlockRotator.transformMetadata(BlockRotator.EAST_DOOR_METADATA, orientation - BlockRotator.EAST_DOOR_METADATA + 2, properties.DimensionalDoorID); int doorOrientation = BlockRotator.transformMetadata(BlockRotator.EAST_DOOR_METADATA, orientation - BlockRotator.EAST_DOOR_METADATA + 2, properties.DimensionalDoorID);