diff --git a/src/main/java/StevenDimDoors/experimental/DirectedGraph.java b/src/main/java/StevenDimDoors/experimental/DirectedGraph.java new file mode 100644 index 00000000..cd56c751 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/DirectedGraph.java @@ -0,0 +1,225 @@ +package StevenDimDoors.experimental; + +/** + * Provides a complete implementation of a directed graph. + * @author SenseiKiwi + * + * @param The type of data to store in the graph's nodes + * @param The type of data to store in the graph's edges + */ +public class DirectedGraph +{ + private static class GraphNode implements IGraphNode + { + private LinkedList> inbound; + private LinkedList> outbound; + private ILinkedListNode> graphEntry; + private P data; + + public GraphNode(P data, LinkedList> graphList) + { + this.data = data; + this.inbound = new LinkedList>(); + this.outbound = new LinkedList>(); + this.graphEntry = graphList.addLast(this); + } + + public int indegree() + { + return inbound.size(); + } + + public int outdegree() + { + return outbound.size(); + } + + public Iterable> inbound() + { + return inbound; + } + + public Iterable> outbound() + { + return outbound; + } + + public P data() + { + return data; + } + + public void remove() + { + graphEntry.remove(); + graphEntry = null; + + for (Edge edge : inbound) + edge.remove(); + + for (Edge edge : outbound) + edge.remove(); + + inbound = null; + outbound = null; + data = null; + } + } + + private static class Edge implements IEdge + { + private GraphNode head; + private GraphNode tail; + private ILinkedListNode> headEntry; + private ILinkedListNode> tailEntry; + private ILinkedListNode> graphEntry; + private Q data; + + public Edge(GraphNode head, GraphNode tail, Q data, LinkedList> graphList) + { + this.head = head; + this.tail = tail; + this.data = data; + this.graphEntry = graphList.addLast(this); + this.headEntry = head.outbound.addLast(this); + this.tailEntry = tail.inbound.addLast(this); + } + + public IGraphNode head() + { + return head; + } + + public IGraphNode tail() + { + return tail; + } + + public Q data() + { + return data; + } + + public void remove() + { + headEntry.remove(); + tailEntry.remove(); + graphEntry.remove(); + headEntry = null; + tailEntry = null; + graphEntry = null; + head = null; + tail = null; + data = null; + } + } + + private LinkedList> nodes; + private LinkedList> edges; + + public DirectedGraph() + { + nodes = new LinkedList>(); + edges = new LinkedList>(); + } + + public int nodeCount() + { + return nodes.size(); + } + + public int edgeCount() + { + return edges.size(); + } + + public boolean isEmpty() + { + return nodes.isEmpty(); + } + + public Iterable> nodes() + { + return nodes; + } + + public Iterable> edges() + { + return edges; + } + + private GraphNode checkNode(IGraphNode node) + { + GraphNode innerNode = (GraphNode) node; + + // Check that this node actually belongs to this graph instance. + // Accepting foreign nodes could corrupt the graph's internal state. + if (innerNode.graphEntry.owner() != nodes) + { + throw new IllegalArgumentException("The specified node does not belong to this graph."); + } + return innerNode; + } + + private Edge checkEdge(IEdge edge) + { + Edge innerEdge = (Edge) edge; + + // Check that this node actually belongs to this graph instance. + // Accepting foreign nodes could corrupt the graph's internal state. + if (innerEdge.graphEntry.owner() != edges) + { + throw new IllegalArgumentException("The specified edge does not belong to this graph."); + } + return innerEdge; + } + + public IGraphNode addNode(U data) + { + return new GraphNode(data, nodes); + } + + public IEdge addEdge(IGraphNode head, IGraphNode tail, V data) + { + GraphNode innerHead = checkNode(head); + GraphNode innerTail = checkNode(tail); + return new Edge(innerHead, innerTail, data, edges); + } + + public U removeNode(IGraphNode node) + { + GraphNode innerNode = checkNode(node); + U data = innerNode.data(); + innerNode.remove(); + return data; + } + + public V removeEdge(IEdge edge) + { + Edge innerEdge = checkEdge(edge); + V data = innerEdge.data(); + innerEdge.remove(); + return data; + } + + public IEdge findEdge(IGraphNode head, IGraphNode tail) + { + for (IEdge edge : head.outbound()) + { + if (edge.tail() == tail) + return edge; + } + return null; + } + + public void clear() + { + // Remove each node individually to guarantee that all external + // references are invalidated. That'll prevent memory leaks and + // keep external code from using removed nodes or edges. + for (GraphNode node : nodes) + { + node.remove(); + } + } +} diff --git a/src/main/java/StevenDimDoors/experimental/DisjointSet.java b/src/main/java/StevenDimDoors/experimental/DisjointSet.java new file mode 100644 index 00000000..4b4150fc --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/DisjointSet.java @@ -0,0 +1,134 @@ +package StevenDimDoors.experimental; + +import java.util.HashMap; + +public class DisjointSet +{ + // This class implements a disjoint set data structure that associates objects with sets. + + private static class SetNode

+ { + private int rank; + private SetNode

parent; + private P data; + + public SetNode(P data) + { + this.data = data; + this.rank = 0; + this.parent = null; + } + } + + private HashMap> mapping; + + public DisjointSet(int initialCapacity) + { + mapping = new HashMap>(initialCapacity); + } + + public boolean makeSet(T element) + { + if (!mapping.containsKey(element)) + { + mapping.put(element, new SetNode(element)); + return true; + } + else + { + return false; + } + } + + private SetNode findRootNode(T element) + { + SetNode node = mapping.get(element); + if (node == null) + { + return null; + } + if (node.parent != null) + { + node.parent = findRootNode(node.parent); + return node.parent; + } + else + { + return node; + } + } + + private SetNode findRootNode(SetNode node) + { + if (node.parent != null) + { + node.parent = findRootNode(node.parent); + return node.parent; + } + else + { + return node; + } + } + + public boolean mergeSets(T first, T second) + { + SetNode firstRoot = findRootNode(first); + SetNode secondRoot = findRootNode(second); + + if (firstRoot == null || secondRoot == null || + firstRoot == secondRoot) + { + return false; + } + + if (firstRoot.rank < secondRoot.rank) + { + firstRoot.parent = secondRoot; + } + else if (firstRoot.rank > secondRoot.rank) + { + secondRoot.parent = firstRoot; + } + else + { + secondRoot.parent = firstRoot; + firstRoot.rank++; + } + return true; + } + + public T find(T element) + { + SetNode root = findRootNode(element); + + if (root != null) + { + return root.data; + } + else + { + return null; + } + } + + public boolean haveSameSet(T first, T second) + { + SetNode firstRoot = findRootNode(first); + SetNode secondRoot = findRootNode(second); + + if (firstRoot == null || secondRoot == null) + { + return false; + } + else + { + return (firstRoot == secondRoot); + } + } + + public void clear() + { + mapping.clear(); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/DoorwayData.java b/src/main/java/StevenDimDoors/experimental/DoorwayData.java new file mode 100644 index 00000000..5d596c2b --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/DoorwayData.java @@ -0,0 +1,51 @@ +package StevenDimDoors.experimental; + +import StevenDimDoors.mod_pocketDim.Point3D; + +public class DoorwayData +{ + public static final char X_AXIS = 'X'; + public static final char Y_AXIS = 'Y'; + public static final char Z_AXIS = 'Z'; + + private Point3D minCorner; + private Point3D maxCorner; + private char axis; + + public DoorwayData(Point3D minCorner, Point3D maxCorner, char axis) + { + this.minCorner = minCorner; + this.maxCorner = maxCorner; + this.axis = axis; + } + + public Point3D minCorner() + { + return minCorner; + } + + public Point3D maxCorner() + { + return maxCorner; + } + + public char axis() + { + return axis; + } + + public int width() + { + return (maxCorner.getX() - minCorner.getX() + 1); + } + + public int height() + { + return (maxCorner.getY() - minCorner.getY() + 1); + } + + public int length() + { + return (maxCorner.getZ() - minCorner.getZ() + 1); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/IEdge.java b/src/main/java/StevenDimDoors/experimental/IEdge.java new file mode 100644 index 00000000..1d268116 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/IEdge.java @@ -0,0 +1,8 @@ +package StevenDimDoors.experimental; + +public interface IEdge +{ + public IGraphNode head(); + public IGraphNode tail(); + public V data(); +} diff --git a/src/main/java/StevenDimDoors/experimental/IGraphNode.java b/src/main/java/StevenDimDoors/experimental/IGraphNode.java new file mode 100644 index 00000000..65fd2fbf --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/IGraphNode.java @@ -0,0 +1,10 @@ +package StevenDimDoors.experimental; + +public interface IGraphNode +{ + public Iterable> inbound(); + public Iterable> outbound(); + public int indegree(); + public int outdegree(); + public U data(); +} diff --git a/src/main/java/StevenDimDoors/experimental/ILinkedListNode.java b/src/main/java/StevenDimDoors/experimental/ILinkedListNode.java new file mode 100644 index 00000000..a7a54adc --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/ILinkedListNode.java @@ -0,0 +1,11 @@ +package StevenDimDoors.experimental; + +public interface ILinkedListNode +{ + public ILinkedListNode next(); + public ILinkedListNode prev(); + public T data(); + public void setData(T data); + public LinkedList owner(); + public T remove(); +} diff --git a/src/main/java/StevenDimDoors/experimental/LinkedList.java b/src/main/java/StevenDimDoors/experimental/LinkedList.java new file mode 100644 index 00000000..98ee0fa7 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/LinkedList.java @@ -0,0 +1,237 @@ +package StevenDimDoors.experimental; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * Provides an implementation of a linked list that exposes its internal nodes. + * This differs from Java's implementation, which does not expose nodes. Access + * to the nodes allows certain operations to be implemented more efficiently. + * Not all operations are supported, but we can add them as the need arises. + * @author SenseiKiwi + * + * @param The type of data to be stored in the LinkedList + */ +public class LinkedList implements Iterable +{ + private static class Node

implements ILinkedListNode

+ { + private Node

next; + private Node

prev; + private P data; + private LinkedList

owner; + + public Node(Node

prev, Node

next, P data, LinkedList

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

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

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

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

implements Iterator

+ { + private Node

current; + private Node

trailer; + + public LinkedListIterator(LinkedList

list) + { + current = list.header.next; + trailer = list.trailer; + } + + @Override + public boolean hasNext() + { + return (current != trailer); + } + + @Override + public P next() + { + if (current == trailer) + { + throw new NoSuchElementException(); + } + else + { + P result = current.data; + current = current.next; + return result; + } + } + + @Override + public void remove() + { + throw new UnsupportedOperationException(); + } + } + + private Node header; // Sentinel node + private Node trailer; // Sentinel node + private int size; + + public LinkedList() + { + size = 0; + header = new Node(null, null, null, this); + trailer = new Node(null, null, null, this); + header.next = trailer; + trailer.prev = header; + } + + public ILinkedListNode header() + { + return header; + } + + public ILinkedListNode trailer() + { + return trailer; + } + + public int size() + { + return size; + } + + public boolean isEmpty() + { + return (size == 0); + } + + public void clear() + { + // Go through the list and wipe everything out + Node current; + Node next; + + size = 0; + current = header.next; + while (current != trailer) + { + next = current.next; + current.clear(); + current = next; + } + header.next = trailer; + trailer.prev = header; + } + + private Node checkNode(ILinkedListNode node) + { + Node innerNode = (Node) node; + + // Check that this node actually belongs to this list instance. + // Accepting foreign nodes could corrupt the list's internal state. + if (innerNode.owner() != this) + { + throw new IllegalArgumentException("The specified node does not belong to this list."); + } + return innerNode; + } + + public ILinkedListNode addFirst(T data) + { + return addAfter(header, data); + } + + public ILinkedListNode addLast(T data) + { + return addBefore(trailer, data); + } + + public ILinkedListNode addBefore(ILinkedListNode node, T data) + { + if (node == header) + { + throw new IllegalArgumentException("Cannot add a node before the header node."); + } + return addAfter( checkNode(node).prev, data ); + } + + public ILinkedListNode addAfter(ILinkedListNode node, T data) + { + if (node == trailer) + { + throw new IllegalArgumentException("Cannot add a node after the trailer node."); + } + return addAfter( checkNode(node), data ); + } + + private Node addAfter(Node node, T data) + { + Node addition = new Node(node, node.next, data, this); + node.next = addition; + addition.next.prev = addition; + size++; + return addition; + } + + public Iterator iterator() + { + return new LinkedListIterator(this); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java new file mode 100644 index 00000000..aa469a46 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -0,0 +1,232 @@ +package StevenDimDoors.experimental; + +import java.util.Random; + +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; + +public class MazeBuilder +{ + private MazeBuilder() { } + + public static void generate(World world, int x, int y, int z, Random random) + { + 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); + + buildRooms(design.getRoomGraph(), world, offset); + carveDoorways(design.getRoomGraph(), world, offset, decay, random); + + applyRandomDestruction(design, world, offset, decay, random); + } + + private static void applyRandomDestruction(MazeDesign design, World world, + Point3D offset, SphereDecayOperation decay, Random random) + { + //final int DECAY_BOX_SIZE = 8 + } + + private static void buildRooms(DirectedGraph roomGraph, World world, Point3D offset) + { + for (IGraphNode node : roomGraph.nodes()) + { + PartitionNode room = node.data(); + buildBox(world, offset, room.minCorner(), room.maxCorner(), Block.stoneBrick.blockID, 0); + } + } + + private static void carveDoorways(DirectedGraph roomGraph, World world, + Point3D offset, SphereDecayOperation decay, Random random) + { + char axis; + Point3D lower; + DoorwayData doorway; + + for (IGraphNode node : roomGraph.nodes()) + { + for (IEdge passage : node.outbound()) + { + 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); + } + } + } + + private static void carveDoorway(World world, char axis, int x, int y, int z, int width, int height, + int length, SphereDecayOperation decay, Random random) + { + final int MIN_DOUBLE_DOOR_SPAN = 10; + + int gap; + switch (axis) + { + case DoorwayData.X_AXIS: + if (length >= MIN_DOUBLE_DOOR_SPAN) + { + gap = (length - 2) / 3; + carveDoorAlongX(world, x, y + 1, z + gap); + carveDoorAlongX(world, x, y + 1, z + length - gap - 1); + } + else if (length > 3) + { + switch (random.nextInt(3)) + { + case 0: + carveDoorAlongX(world, x, y + 1, z + (length - 1) / 2); + break; + case 1: + carveDoorAlongX(world, x, y + 1, z + 2); + break; + case 2: + carveDoorAlongX(world, x, y + 1, z + length - 3); + break; + } + } + else + { + carveDoorAlongX(world, x, y + 1, z + 1); + } + break; + case DoorwayData.Z_AXIS: + if (width >= MIN_DOUBLE_DOOR_SPAN) + { + gap = (width - 2) / 3; + carveDoorAlongZ(world, x + gap, y + 1, z); + carveDoorAlongZ(world, x + width - gap - 1, y + 1, z); + } + else if (length > 3) + { + switch (random.nextInt(3)) + { + case 0: + carveDoorAlongZ(world, x + (width - 1) / 2, y + 1, z); + break; + case 1: + carveDoorAlongZ(world, x + 2, y + 1, z); + break; + case 2: + carveDoorAlongZ(world, x + width - 3, y + 1, z); + break; + } + } + else + { + carveDoorAlongZ(world, x + 1, y + 1, z); + } + break; + case DoorwayData.Y_AXIS: + gap = Math.min(width, length) - 2; + if (gap > 1) + { + if (gap > 6) + { + gap = 6; + } + decay.apply(world, + x + random.nextInt(width - gap - 1) + 1, y - 1, + z + random.nextInt(length - gap - 1) + 1, gap, 4, gap); + } + else + { + carveHole(world, x + 1, y, z + 1); + } + break; + } + } + + private static void carveDoorAlongX(World world, int x, int y, int z) + { + setBlockDirectly(world, x, y, z, 0, 0); + setBlockDirectly(world, x, y + 1, z, 0, 0); + setBlockDirectly(world, x + 1, y, z, 0, 0); + setBlockDirectly(world, x + 1, y + 1, z, 0, 0); + } + + private static void carveDoorAlongZ(World world, int x, int y, int z) + { + setBlockDirectly(world, x, y, z, 0, 0); + setBlockDirectly(world, x, y + 1, z, 0, 0); + setBlockDirectly(world, x, y, z + 1, 0, 0); + setBlockDirectly(world, x, y + 1, z + 1, 0, 0); + } + + private static void carveHole(World world, int x, int y, int z) + { + setBlockDirectly(world, x, y, 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) + { + int minX = minCorner.getX() + offset.getX(); + int minY = minCorner.getY() + offset.getY(); + int minZ = minCorner.getZ() + offset.getZ(); + + int maxX = maxCorner.getX() + offset.getX(); + int maxY = maxCorner.getY() + offset.getY(); + int maxZ = maxCorner.getZ() + offset.getZ(); + + int x, y, z; + + for (x = minX; x <= maxX; x++) + { + for (z = minZ; z <= maxZ; z++) + { + setBlockDirectly(world, x, minY, z, blockID, metadata); + setBlockDirectly(world, x, maxY, z, blockID, metadata); + } + } + for (x = minX; x <= maxX; x++) + { + for (y = minY; y <= maxY; y++) + { + setBlockDirectly(world, x, y, minZ, blockID, metadata); + setBlockDirectly(world, x, y, maxZ, blockID, metadata); + } + } + for (z = minZ; z <= maxZ; z++) + { + for (y = minY; y <= maxY; y++) + { + setBlockDirectly(world, minX, y, z, blockID, metadata); + setBlockDirectly(world, maxX, y, z, blockID, metadata); + } + } + } + + private static void setBlockDirectly(World world, int x, int y, int z, int blockID, int metadata) + { + if (blockID != 0 && Block.blocksList[blockID] == null) + { + return; + } + + int cX = x >> 4; + int cZ = z >> 4; + int cY = y >> 4; + Chunk chunk; + + int localX = (x % 16) < 0 ? (x % 16) + 16 : (x % 16); + int localZ = (z % 16) < 0 ? (z % 16) + 16 : (z % 16); + ExtendedBlockStorage extBlockStorage; + + chunk = world.getChunkFromChunkCoords(cX, cZ); + extBlockStorage = chunk.getBlockStorageArray()[cY]; + if (extBlockStorage == null) + { + extBlockStorage = new ExtendedBlockStorage(cY << 4, !world.provider.hasNoSky); + chunk.getBlockStorageArray()[cY] = extBlockStorage; + } + extBlockStorage.setExtBlockID(localX, y & 15, localZ, blockID); + extBlockStorage.setExtBlockMetadata(localX, y & 15, localZ, metadata); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesign.java b/src/main/java/StevenDimDoors/experimental/MazeDesign.java new file mode 100644 index 00000000..d71b9566 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/MazeDesign.java @@ -0,0 +1,48 @@ +package StevenDimDoors.experimental; + +import java.util.ArrayList; + +public class MazeDesign +{ + private PartitionNode root; + private DirectedGraph rooms; + private ArrayList> cores; + + public MazeDesign(PartitionNode root, DirectedGraph rooms, + ArrayList> cores) + { + this.root = root; + this.rooms = rooms; + this.cores = cores; + } + + public PartitionNode getRootPartition() + { + return root; + } + + public DirectedGraph getRoomGraph() + { + return rooms; + } + + public ArrayList> getCoreNodes() + { + return cores; + } + + public int width() + { + return root.width(); + } + + public int height() + { + return root.height(); + } + + public int length() + { + return root.length(); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java new file mode 100644 index 00000000..289931a5 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java @@ -0,0 +1,579 @@ +package StevenDimDoors.experimental; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Random; +import java.util.Stack; + +import net.minecraft.util.MathHelper; +import StevenDimDoors.mod_pocketDim.Point3D; + +public class MazeDesigner +{ + private static final int MAZE_WIDTH = 34; + private static final int MAZE_LENGTH = 34; + private static final int MAZE_HEIGHT = 20; + private static final int MIN_HEIGHT = 4; + private static final int MIN_SIDE = 3; + private static final int SPLIT_COUNT = 9; + + private MazeDesigner() { } + + public static MazeDesign generate(Random random) + { + // Construct a random binary space partitioning of our maze volume + PartitionNode root = partitionRooms(MAZE_WIDTH, MAZE_HEIGHT, MAZE_LENGTH, SPLIT_COUNT, random); + + // List all the leaf nodes of the partition tree, which denote individual rooms + ArrayList partitions = new ArrayList(1 << SPLIT_COUNT); + listRoomPartitions(root, partitions); + + // Construct an adjacency graph of the rooms we've carved out. Two rooms are + // considered adjacent if and only if a doorway could connect them. Their + // common boundary must be large enough for a doorway. + DirectedGraph rooms = createRoomGraph(root, partitions, random); + + // Cut out random subgraphs from the adjacency graph + ArrayList> cores = createMazeSections(rooms, random); + + // Remove unnecessary passages through floors/ceilings and some from the walls + for (IGraphNode core : cores) + { + pruneDoorways(core, rooms, random); + } + + return new MazeDesign(root, rooms, cores); + } + + private static void listRoomPartitions(PartitionNode node, ArrayList partitions) + { + if (node.isLeaf()) + { + partitions.add(node); + } + else + { + listRoomPartitions(node.leftChild(), partitions); + listRoomPartitions(node.rightChild(), partitions); + } + } + + private static void removeRoomPartitions(PartitionNode node) + { + // Remove a node and any of its ancestors that become leaf nodes + PartitionNode parent; + PartitionNode current; + + current = node; + while (current != null && current.isLeaf()) + { + parent = current.parent(); + current.remove(); + current = parent; + } + } + + private static PartitionNode partitionRooms(int width, int height, int length, int maxLevels, Random random) + { + PartitionNode root = new PartitionNode(width, height, length); + splitByRandomX(root, maxLevels, random); + return root; + } + + private static void splitByRandomX(PartitionNode node, int levels, Random random) + { + if (node.width() >= 2 * MIN_SIDE) + { + node.splitByX(MathHelper.getRandomIntegerInRange(random, + node.minCorner().getX() + MIN_SIDE, node.maxCorner().getX() - MIN_SIDE + 1)); + + if (levels > 1) + { + splitByRandomZ(node.leftChild(), levels - 1, random); + splitByRandomZ(node.rightChild(), levels - 1, random); + } + } + else if (levels > 1) + { + splitByRandomZ(node, levels - 1, random); + } + } + + private static void splitByRandomZ(PartitionNode node, int levels, Random random) + { + if (node.length() >= 2 * MIN_SIDE) + { + node.splitByZ(MathHelper.getRandomIntegerInRange(random, + node.minCorner().getZ() + MIN_SIDE, node.maxCorner().getZ() - MIN_SIDE + 1)); + + if (levels > 1) + { + splitByRandomY(node.leftChild(), levels - 1, random); + splitByRandomY(node.rightChild(), levels - 1, random); + } + } + else if (levels > 1) + { + splitByRandomY(node, levels - 1, random); + } + } + + private static void splitByRandomY(PartitionNode node, int levels, Random random) + { + if (node.height() >= 2 * MIN_HEIGHT) + { + node.splitByY(MathHelper.getRandomIntegerInRange(random, + node.minCorner().getY() + MIN_HEIGHT, node.maxCorner().getY() - MIN_HEIGHT + 1)); + + if (levels > 1) + { + splitByRandomX(node.leftChild(), levels - 1, random); + splitByRandomX(node.rightChild(), levels - 1, random); + } + } + else if (levels > 1) + { + splitByRandomX(node, levels - 1, random); + } + } + + private static DirectedGraph createRoomGraph(PartitionNode root, ArrayList partitions, Random random) + { + DirectedGraph roomGraph = new DirectedGraph(); + HashMap> roomsToGraph = new HashMap>(2 * partitions.size()); + + // Shuffle the list of rooms so that they're not listed in any ordered way in the room graph + // This is the only convenient way of randomizing the maze sections generated later + Collections.shuffle(partitions, random); + + // Add all rooms to a graph + // Also add them to a map so we can associate rooms with their graph nodes + // The map is needed for linking graph nodes based on adjacent partitions + for (PartitionNode partition : partitions) + { + roomsToGraph.put(partition, roomGraph.addNode(partition)); + } + + // Add edges for each room + for (IGraphNode node : roomGraph.nodes()) + { + findDoorways(node, root, roomsToGraph, roomGraph); + } + + return roomGraph; + } + + private static void findDoorways(IGraphNode roomNode, PartitionNode root, + HashMap> roomsToGraph, + DirectedGraph roomGraph) + { + // This function finds rooms adjacent to a specified room that could be connected + // to it through a doorway. Edges are added to the room graph to denote rooms that + // could be connected. The areas of their common bounds that could be carved + // out for a passage are stored in the edges. + + // Three directions have to be checked: up, forward, and right. The other three + // directions (down, back, left) aren't checked because other nodes will cover them. + // That is, down for this room is up for some other room, if it exists. Also, rooms + // are guaranteed to have at least one doorway to another room, because the minimum + // dimensions to which a room can be partitioned still allow passages along all + // its sides. A room's sibling in the partition tree is guaranteed to share a side + // through which a doorway could exist. Similar arguments guarantee the existence + // of passages such that the whole set of rooms is a connected graph - in other words, + // there will always be a way to walk from any room to any other room. + + boolean[][] detected; + PartitionNode adjacent; + + int a, b, c; + int p, q, r; + int minXI, minYI, minZI; + int maxXI, maxYI, maxZI; + Point3D otherMin; + Point3D otherMax; + DoorwayData doorway; + IGraphNode adjacentNode; + + PartitionNode room = roomNode.data(); + Point3D minCorner = room.minCorner(); + Point3D maxCorner = room.maxCorner(); + + int minX = minCorner.getX(); + int minY = minCorner.getY(); + int minZ = minCorner.getZ(); + + int maxX = maxCorner.getX(); + int maxY = maxCorner.getY(); + int maxZ = maxCorner.getZ(); + + int width = room.width(); + int height = room.height(); + int length = room.length(); + + if (maxZ < root.maxCorner().getZ()) + { + // Check for adjacent rooms along the XY plane + detected = new boolean[width][height]; + for (a = 0; a < width; a++) + { + for (b = 0; b < height; b++) + { + if (!detected[a][b]) + { + adjacent = root.findPoint(minX + a, minY + b, maxZ + 1); + if (adjacent != null) + { + // Compute the dimensions available for a doorway + otherMin = adjacent.minCorner(); + otherMax = adjacent.maxCorner(); + minXI = Math.max(minX, otherMin.getX()); + maxXI = Math.min(maxX, otherMax.getX()); + minYI = Math.max(minY, otherMin.getY()); + maxYI = Math.min(maxY, otherMax.getY()); + + for (p = 0; p <= maxXI - minXI; p++) + { + for (q = 0; q <= maxYI - minYI; q++) + { + detected[p + a][q + b] = true; + } + } + // Check if we meet the minimum dimensions needed for a doorway + if (maxXI - minXI + 1 >= MIN_SIDE && maxYI - minYI + 1 >= MIN_HEIGHT) + { + otherMin = new Point3D(minXI, minYI, maxZ); + otherMax = new Point3D(maxXI, maxYI, maxZ + 1); + doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Z_AXIS); + adjacentNode = roomsToGraph.get(adjacent); + roomGraph.addEdge(roomNode, adjacentNode, doorway); + } + } + else + { + detected[a][b] = true; + } + } + } + } + } + + + if (maxX < root.maxCorner().getX()) + { + // Check for adjacent rooms along the YZ plane + detected = new boolean[height][length]; + for (b = 0; b < height; b++) + { + for (c = 0; c < length; c++) + { + if (!detected[b][c]) + { + adjacent = root.findPoint(maxX + 1, minY + b, minZ + c); + if (adjacent != null) + { + // Compute the dimensions available for a doorway + otherMin = adjacent.minCorner(); + otherMax = adjacent.maxCorner(); + minYI = Math.max(minY, otherMin.getY()); + maxYI = Math.min(maxY, otherMax.getY()); + minZI = Math.max(minZ, otherMin.getZ()); + maxZI = Math.min(maxZ, otherMax.getZ()); + + for (q = 0; q <= maxYI - minYI; q++) + { + for (r = 0; r <= maxZI - minZI; r++) + { + detected[q + b][r + c] = true; + } + } + // Check if we meet the minimum dimensions needed for a doorway + if (maxYI - minYI + 1 >= MIN_HEIGHT && maxZI - minZI + 1 >= MIN_SIDE) + { + otherMin = new Point3D(maxX, minYI, minZI); + otherMax = new Point3D(maxX + 1, maxYI, maxZI); + doorway = new DoorwayData(otherMin, otherMax, DoorwayData.X_AXIS); + adjacentNode = roomsToGraph.get(adjacent); + roomGraph.addEdge(roomNode, adjacentNode, doorway); + } + } + else + { + detected[b][c] = true; + } + } + } + } + } + + + if (maxY < root.maxCorner().getY()) + { + // Check for adjacent rooms along the XZ plane + detected = new boolean[width][length]; + for (a = 0; a < width; a++) + { + for (c = 0; c < length; c++) + { + if (!detected[a][c]) + { + adjacent = root.findPoint(minX + a, maxY + 1, minZ + c); + if (adjacent != null) + { + // Compute the dimensions available for a doorway + otherMin = adjacent.minCorner(); + otherMax = adjacent.maxCorner(); + minXI = Math.max(minX, otherMin.getX()); + maxXI = Math.min(maxX, otherMax.getX()); + minZI = Math.max(minZ, otherMin.getZ()); + maxZI = Math.min(maxZ, otherMax.getZ()); + + for (p = 0; p <= maxXI - minXI; p++) + { + for (r = 0; r <= maxZI - minZI; r++) + { + detected[p + a][r + c] = true; + } + } + // Check if we meet the minimum dimensions needed for a doorway + if (maxXI - minXI + 1 >= MIN_SIDE && maxZI - minZI + 1 >= MIN_SIDE) + { + otherMin = new Point3D(minXI, maxY, minZI); + otherMax = new Point3D(maxXI, maxY + 1, maxZI); + doorway = new DoorwayData(otherMin, otherMax, DoorwayData.Y_AXIS); + adjacentNode = roomsToGraph.get(adjacent); + roomGraph.addEdge(roomNode, adjacentNode, doorway); + } + } + else + { + detected[a][c] = true; + } + } + } + } + } + + //Done! + } + + private static ArrayList> createMazeSections(DirectedGraph roomGraph, Random random) + { + // The randomness of the sections generated here hinges on + // the nodes in the graph being in a random order. We assume + // that was handled in a previous step! + + final int MAX_DISTANCE = 2; + final int MIN_SECTION_ROOMS = 5; + + int distance; + IGraphNode current; + IGraphNode neighbor; + + ArrayList> cores = new ArrayList>(); + ArrayList> removals = new ArrayList>(); + ArrayList> section = new ArrayList>(); + + Queue> ordering = new LinkedList>(); + HashMap, Integer> distances = new HashMap, Integer>(); + + // Repeatedly generate sections until all nodes have been visited + for (IGraphNode node : roomGraph.nodes()) + { + // If this node hasn't been visited, then use it as the core of a new section + // Otherwise, ignore it, since it was already processed + if (!distances.containsKey(node)) + { + // Perform a breadth-first search to tag surrounding nodes with distances + distances.put(node, 0); + ordering.add(node); + section.clear(); + + while (!ordering.isEmpty()) + { + current = ordering.remove(); + distance = distances.get(current) + 1; + + if (distance <= MAX_DISTANCE + 1) + { + section.add(current); + + // Visit neighboring nodes and assign them distances, if they don't + // have a distance assigned already + for (IEdge edge : current.inbound()) + { + neighbor = edge.head(); + if (!distances.containsKey(neighbor)) + { + distances.put(neighbor, distance); + ordering.add(neighbor); + } + } + for (IEdge edge : current.outbound()) + { + neighbor = edge.tail(); + if (!distances.containsKey(neighbor)) + { + distances.put(neighbor, distance); + ordering.add(neighbor); + } + } + } + else + { + removals.add(current); + break; + } + } + + // List nodes that have a distance of exactly MAX_DISTANCE + 1 + // Those are precisely the nodes that remain in the queue + // We can't remove them immediately because that could break + // the iterator for the graph. + while (!ordering.isEmpty()) + { + removals.add(ordering.remove()); + } + + // Check if this section contains enough rooms + if (section.size() >= MIN_SECTION_ROOMS) + { + cores.add(node); + } + else + { + removals.addAll(section); + } + } + } + + // Remove all the nodes that were listed for removal + // Also remove unused partitions from the partition tree + for (IGraphNode node : removals) + { + removeRoomPartitions(node.data()); + roomGraph.removeNode(node); + } + return cores; + } + + private static void pruneDoorways(IGraphNode core, + DirectedGraph rooms, Random random) + { + // 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 + // still allowing any room to be reachable from any other room. + // In technical terms, we receive a node from a connected subgraph + // and we need to remove as many Y_AXIS-type edges as possible while + // preserving connectedness. We also want to randomly remove some of + // the other doorways without breaking connectedness. + + // An efficient solution is to assign nodes to disjoint sets based + // on their components, ignoring all Y_AXIS edges, then iterate over + // a list of those edges and remove them if they connect two nodes + // in the same set. Otherwise, merge their sets and keep the edge. + // This is similar to algorithms for spanning trees. The same + // idea applies for the other doorways, plus some randomness. + + // First, list all nodes in the subgraph + IGraphNode current; + IGraphNode neighbor; + + Stack> ordering = new Stack>(); + ArrayList> subgraph = new ArrayList>(64); + DisjointSet> components = new DisjointSet>(128); + + ordering.add(core); + components.makeSet(core); + while (!ordering.isEmpty()) + { + current = ordering.pop(); + subgraph.add(current); + + for (IEdge edge : current.inbound()) + { + neighbor = edge.head(); + if (components.makeSet(neighbor)) + { + ordering.add(neighbor); + } + } + for (IEdge edge : current.outbound()) + { + neighbor = edge.tail(); + if (components.makeSet(neighbor)) + { + ordering.add(neighbor); + } + } + } + + // Now iterate over the list of nodes and merge their sets + // We only have to look at outbound edges since inbound edges mirror them + // Also list any Y_AXIS doorways we come across + ArrayList> targets = + new ArrayList>(); + + for (IGraphNode room : subgraph) + { + for (IEdge passage : room.outbound()) + { + if (passage.data().axis() != DoorwayData.Y_AXIS) + { + components.mergeSets(passage.head(), passage.tail()); + } + else + { + targets.add(passage); + } + } + } + + // Shuffle the list of doorways to randomize which ones are removed + Collections.shuffle(targets, random); + + // Merge sets together and remove unnecessary doorways + for (IEdge passage : targets) + { + if (!components.mergeSets(passage.head(), passage.tail())) + { + rooms.removeEdge(passage); + } + } + + // Repeat the pruning process with X_AXIS and Z_AXIS doorways + // In this case, unnecessary edges might be kept at random + components.clear(); + targets.clear(); + + for (IGraphNode room : subgraph) + { + components.makeSet(room); + } + for (IGraphNode room : subgraph) + { + for (IEdge passage : room.outbound()) + { + if (passage.data().axis() == DoorwayData.Y_AXIS) + { + components.mergeSets(passage.head(), passage.tail()); + } + else + { + targets.add(passage); + } + } + } + Collections.shuffle(targets, random); + for (IEdge passage : targets) + { + if (!components.mergeSets(passage.head(), passage.tail()) && random.nextBoolean()) + { + rooms.removeEdge(passage); + } + } + } + +} diff --git a/src/main/java/StevenDimDoors/experimental/PartitionNode.java b/src/main/java/StevenDimDoors/experimental/PartitionNode.java new file mode 100644 index 00000000..25d8d6d1 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/PartitionNode.java @@ -0,0 +1,161 @@ +package StevenDimDoors.experimental; + +import StevenDimDoors.mod_pocketDim.Point3D; + +public class PartitionNode +{ + private Point3D minCorner; + private Point3D maxCorner; + private PartitionNode parent; + private PartitionNode leftChild = null; + private PartitionNode rightChild = null; + + public PartitionNode(int width, int height, int length) + { + parent = null; + minCorner = new Point3D(0, 0, 0); + maxCorner = new Point3D(width - 1, height - 1, length - 1); + } + + private PartitionNode(PartitionNode parent, Point3D minCorner, Point3D maxCorner) + { + this.parent = parent; + this.minCorner = minCorner; + this.maxCorner = maxCorner; + } + + public int width() + { + return (maxCorner.getX() - minCorner.getX() + 1); + } + + public int height() + { + return (maxCorner.getY() - minCorner.getY() + 1); + } + + public int length() + { + return (maxCorner.getZ() - minCorner.getZ() + 1); + } + + public boolean isLeaf() + { + return (leftChild == null && rightChild == null); + } + + public PartitionNode leftChild() + { + return leftChild; + } + + public PartitionNode rightChild() + { + return rightChild; + } + + public Point3D minCorner() + { + return minCorner; + } + + public Point3D maxCorner() + { + return maxCorner; + } + + public PartitionNode parent() + { + return parent; + } + + public void splitByX(int rightStart) + { + if (!this.isLeaf()) + { + throw new IllegalStateException("This node has already been split."); + } + if (rightStart <= minCorner.getX() || rightStart > maxCorner.getX()) + { + throw new IllegalArgumentException("The specified cutting plane is invalid."); + } + leftChild = new PartitionNode(this, minCorner, new Point3D(rightStart - 1, maxCorner.getY(), maxCorner.getZ())); + rightChild = new PartitionNode(this, new Point3D(rightStart, minCorner.getY(), minCorner.getZ()), maxCorner); + } + + public void splitByY(int rightStart) + { + if (!this.isLeaf()) + { + throw new IllegalStateException("This node has already been split."); + } + if (rightStart <= minCorner.getY() || rightStart > maxCorner.getY()) + { + throw new IllegalArgumentException("The specified cutting plane is invalid."); + } + leftChild = new PartitionNode(this, minCorner, new Point3D(maxCorner.getX(), rightStart - 1, maxCorner.getZ())); + rightChild = new PartitionNode(this, new Point3D(minCorner.getX(), rightStart, minCorner.getZ()), maxCorner); + } + + public void splitByZ(int rightStart) + { + if (!this.isLeaf()) + { + throw new IllegalStateException("This node has already been split."); + } + if (rightStart <= minCorner.getZ() || rightStart > maxCorner.getZ()) + { + throw new IllegalArgumentException("The specified cutting plane is invalid."); + } + leftChild = new PartitionNode(this, minCorner, new Point3D(maxCorner.getX(), maxCorner.getY(), rightStart - 1)); + rightChild = new PartitionNode(this, new Point3D(minCorner.getX(), minCorner.getY(), rightStart), maxCorner); + } + + public void remove() + { + if (parent != null) + { + if (parent.leftChild == this) + parent.leftChild = null; + else + parent.rightChild = null; + parent = null; + } + } + + public boolean contains(int x, int y, int z) + { + return ((minCorner.getX() <= x && x <= maxCorner.getX()) && + (minCorner.getY() <= y && y <= maxCorner.getY()) && + (minCorner.getZ() <= z && z <= maxCorner.getZ())); + } + + public PartitionNode findPoint(int x, int y, int z) + { + // Find the lowest node that contains the specified point or return null + if (this.contains(x, y, z)) + { + return this.findPointInternal(x, y, z); + } + else + { + return null; + } + } + + private PartitionNode findPointInternal(int x, int y, int z) + { + if (leftChild != null && leftChild.contains(x, y, z)) + { + return leftChild.findPointInternal(x, y, z); + } + else if (rightChild != null && rightChild.contains(x, y, z)) + { + return rightChild.findPointInternal(x, y, z); + } + else + { + return this; + } + } +} diff --git a/src/main/java/StevenDimDoors/experimental/SphereDecayOperation.java b/src/main/java/StevenDimDoors/experimental/SphereDecayOperation.java new file mode 100644 index 00000000..95587b64 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/SphereDecayOperation.java @@ -0,0 +1,84 @@ +package StevenDimDoors.experimental; + +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.BlockContainer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityChest; +import net.minecraft.tileentity.TileEntityDispenser; +import net.minecraft.world.World; +import StevenDimDoors.mod_pocketDim.DDLoot; +import StevenDimDoors.mod_pocketDim.DDProperties; +import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.schematic.WorldOperation; + +/** + * Provides an operation for damaging structures based on a spherical area. The chance of damage decreases + * with the square of the distance from the center of the sphere. + * @author SenseiKiwi + * + */ +public class SphereDecayOperation extends WorldOperation +{ + private Random random; + private double scaling; + private double centerX; + private double centerY; + private double centerZ; + private int primaryBlockID; + private int primaryMetadata; + private int secondaryBlockID; + private int secondaryMetadata; + + public SphereDecayOperation(Random random, int primaryBlockID, int primaryMetadata, int secondaryBlockID, int secondaryMetadata) + { + super("SphereDecayOperation"); + this.random = random; + this.primaryBlockID = primaryBlockID; + this.primaryMetadata = primaryMetadata; + this.secondaryBlockID = secondaryBlockID; + this.secondaryMetadata = secondaryMetadata; + } + + @Override + protected boolean initialize(World world, int x, int y, int z, int width, int height, int length) + { + // Calculate a scaling factor so that the probability of decay + // at the edge of the largest dimension of our bounds is 20%. + scaling = Math.max(width - 1, Math.max(height - 1, length - 1)) / 2.0; + scaling *= scaling * 0.20; + + centerX = x + width / 2.0; + centerY = y + height / 2.0; + centerZ = z + length / 2.0; + return true; + } + + @Override + protected boolean applyToBlock(World world, int x, int y, int z) + { + // Don't raise any notifications. This operation is only designed to run + // when a dimension is being generated, which means there are no players around. + if (!world.isAirBlock(x, y, z)) + { + double dx = (centerX - x - 0.5); + double dy = (centerY - y - 0.5); + double dz = (centerZ - z - 0.5); + double squareDistance = dx * dx + dy * dy + dz * dz; + + if (squareDistance < 0.5 || random.nextDouble() < scaling / squareDistance) + { + world.setBlock(x, y, z, primaryBlockID, primaryMetadata, 1); + } + else if (random.nextDouble() < scaling / squareDistance) + { + world.setBlock(x, y, z, secondaryBlockID, secondaryMetadata, 1); + } + } + return true; + } +} diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java b/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java index 204edb8c..f639ce18 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java @@ -61,10 +61,12 @@ public class Point3D implements Serializable { { return new Point3D(x, y, z); } + public int[] toIntArray() { return new int[]{x,y,z}; } + public boolean equals(Point3D other) { if (other == null) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java index 54f844f0..e0084c64 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java @@ -25,7 +25,6 @@ import StevenDimDoors.mod_pocketDim.tileentities.TileEntityDimDoor; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; -@SuppressWarnings("deprecation") public abstract class BaseDimDoor extends BlockDoor implements IDimDoor, ITileEntityProvider { protected final DDProperties properties; @@ -429,4 +428,11 @@ public abstract class BaseDimDoor extends BlockDoor implements IDimDoor, ITileEn int direction = MathHelper.floor_double((entity.rotationYaw + 90) * 4.0F / 360.0F + 0.5D) & 3; return ((metadata & 3) == direction); } + + @Override + public void initDoorTE(World world, int x, int y, int z) + { + TileEntity te = this.createNewTileEntity(world); + world.setBlockTileEntity(x, y, z, te); + } } \ No newline at end of file diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/IDimDoor.java b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/IDimDoor.java index 39bfcd20..e5939152 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/IDimDoor.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/IDimDoor.java @@ -10,4 +10,6 @@ public interface IDimDoor public void placeLink(World world, int x, int y, int z); public int getDrops(); + + public void initDoorTE(World world, int x, int y, int z); } diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/TransTrapdoor.java b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/TransTrapdoor.java index 1ff68132..6eac539b 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/TransTrapdoor.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/TransTrapdoor.java @@ -118,4 +118,11 @@ public class TransTrapdoor extends BlockTrapDoor implements IDimDoor, ITileEntit { return (metadata & 8) == 0; } + + @Override + public void initDoorTE(World world, int x, int y, int z) + { + TileEntity te = this.createNewTileEntity(world); + world.setBlockTileEntity(x, y, z, te); + } } \ No newline at end of file diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java b/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java index a27b86a2..2df0f186 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java @@ -300,7 +300,6 @@ public class PocketManager PocketManager.dimensionData.put(dimData.id, dimData); dimWatcher.onCreated(new ClientDimData(dimData)); - return true; } public static boolean deletePocket(NewDimData target, boolean deleteFolder) @@ -403,7 +402,7 @@ public class PocketManager */ private static void loadInternal() { - System.out.println(!FMLCommonHandler.instance().getSide().isClient()); + //System.out.println(!FMLCommonHandler.instance().getSide().isClient()); File saveDir = DimensionManager.getCurrentSaveRootDirectory(); if (saveDir != null) @@ -507,14 +506,24 @@ public class PocketManager DimensionManager.registerDimension(dimensionID, properties.PocketProviderID); return registerDimension(dimensionID, (InnerDimData) parent, true, isDungeon); } - + /** + * Registers a dimension with DD but NOT with forge. + * @param dimensionID + * @param parent + * @param isPocket + * @param isDungeon + * @return + */ private static NewDimData registerDimension(int dimensionID, InnerDimData parent, boolean isPocket, boolean isDungeon) - { + { if (dimensionData.containsKey(dimensionID)) { + if(PocketManager.dimensionIDBlackList.contains(dimensionID)) + { + throw new IllegalArgumentException("Cannot register a dimension with ID = " + dimensionID + " because it has been blacklisted."); + } throw new IllegalArgumentException("Cannot register a dimension with ID = " + dimensionID + " because it has already been registered."); } - //TODO blacklist stuff probably should happen here InnerDimData dimension = new InnerDimData(dimensionID, parent, isPocket, isDungeon, linkWatcher); dimensionData.put(dimensionID, dimension); if (!dimension.isPocketDimension()) @@ -529,6 +538,7 @@ public class PocketManager @SideOnly(Side.CLIENT) private static NewDimData registerClientDimension(int dimensionID, int rootID) { + System.out.println("Registered dim "+dimensionID+" on the client."); // No need to raise events heres since this code should only run on the client side // getDimensionData() always handles root dimensions properly, even if the weren't defined before @@ -560,7 +570,7 @@ public class PocketManager //Steven DimensionManager.registerDimension(dimensionID, mod_pocketDim.properties.PocketProviderID); } - return dimension; + return dimension; } public static NewDimData getDimensionData(World world) @@ -579,6 +589,7 @@ public class PocketManager if(PocketManager.dimensionData == null) { System.out.println("Something odd happend during shutdown"); + return null; } NewDimData dimension = PocketManager.dimensionData.get(dimensionID); if (dimension == null) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java b/src/main/java/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java index c3eec44e..621fc48c 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/dungeon/DungeonSchematic.java @@ -17,10 +17,13 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import StevenDimDoors.mod_pocketDim.DDProperties; import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.mod_pocketDim; +import StevenDimDoors.mod_pocketDim.blocks.IDimDoor; import StevenDimDoors.mod_pocketDim.core.DimLink; import StevenDimDoors.mod_pocketDim.core.LinkTypes; import StevenDimDoors.mod_pocketDim.core.NewDimData; import StevenDimDoors.mod_pocketDim.core.PocketManager; +import StevenDimDoors.mod_pocketDim.items.ItemDimensionalDoor; import StevenDimDoors.mod_pocketDim.schematic.BlockRotator; import StevenDimDoors.mod_pocketDim.schematic.CompoundFilter; import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException; @@ -293,6 +296,8 @@ public class DungeonSchematic extends Schematic { Point4D destination = entryLink.source(); NewDimData prevDim = PocketManager.getDimensionData(destination.getDimension()); prevDim.setDestination(reverseLink, destination.getX(), destination.getY(), destination.getZ()); + initDoorTileEntity(world, pocketCenter); + } private static void createExitDoorLink(World world, NewDimData dimension, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter) @@ -313,6 +318,8 @@ public class DungeonSchematic extends Schematic { int metadata = world.getBlockMetadata(x, y, z); setBlockDirectly(world, x, y + 1, z, blockID, metadata); } + initDoorTileEntity(world, location); + } private static void createDimensionalDoorLink(NewDimData dimension, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter,World world) @@ -323,6 +330,9 @@ public class DungeonSchematic extends Schematic { int orientation = world.getBlockMetadata(location.getX(), location.getY()-1, location.getZ()); dimension.createLink(location.getX(), location.getY(), location.getZ(), LinkTypes.DUNGEON, orientation); + initDoorTileEntity(world, location); + + } private static void spawnMonolith(World world, Point3D point, Point3D entrance, int rotation, Point3D pocketCenter, boolean canSpawn) @@ -340,4 +350,21 @@ public class DungeonSchematic extends Schematic { world.spawnEntityInWorld(mob); } } + private static void initDoorTileEntity(World world, Point3D point) + { + Block door = Block.blocksList[world.getBlockId(point.getX(), point.getY(), point.getZ())]; + Block door2 = Block.blocksList[world.getBlockId(point.getX(), point.getY()-1, point.getZ())]; + + if(door instanceof IDimDoor&&door2 instanceof IDimDoor) + { + ((IDimDoor) door).initDoorTE(world, point.getX(), point.getY(), point.getZ()); + ((IDimDoor) door).initDoorTE(world, point.getX(), point.getY()-1, point.getZ()); + + } + else + { + throw new IllegalArgumentException("Tried to init a dim door TE on a block that isnt a Dim Door!!"); + } + + } } diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/tileentities/TileEntityDimDoor.java b/src/main/java/StevenDimDoors/mod_pocketDim/tileentities/TileEntityDimDoor.java index dcf8b0ad..12fcd654 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/tileentities/TileEntityDimDoor.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/tileentities/TileEntityDimDoor.java @@ -19,15 +19,14 @@ public class TileEntityDimDoor extends TileEntity @Override public boolean canUpdate() { - return false; + return true; } @Override public void updateEntity() { - } - + @Override public Packet getDescriptionPacket() { if(PocketManager.getLink(xCoord, yCoord, zCoord, worldObj)!=null) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java b/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java index fc52d2f4..3470cd17 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java @@ -8,6 +8,7 @@ import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import net.minecraftforge.common.DimensionManager; +import StevenDimDoors.experimental.MazeBuilder; import StevenDimDoors.mod_pocketDim.DDProperties; import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.blocks.IDimDoor; @@ -471,6 +472,7 @@ public class PocketBuilder Point3D door = new Point3D(x, y, z); BlockRotator.transformPoint(center, door, orientation - BlockRotator.EAST_DOOR_METADATA, door); + //Build the outer layer of Eternal Fabric buildBox(world, center.getX(), center.getY(), center.getZ(), (size / 2), properties.PermaFabricBlockID, false, 0); @@ -481,6 +483,9 @@ public class PocketBuilder layer < (wallThickness - 1) && properties.TNFREAKINGT_Enabled, properties.NonTntWeight); } + + //MazeBuilder.generate(world, x, y, z, random); + //Build the door int doorOrientation = BlockRotator.transformMetadata(BlockRotator.EAST_DOOR_METADATA, orientation - BlockRotator.EAST_DOOR_METADATA + 2, properties.DimensionalDoorID); ItemDimensionalDoor.placeDoorBlock(world, x, y - 1, z, doorOrientation, doorBlock);