commit
ab5ce1e652
20 changed files with 1849 additions and 10 deletions
225
src/main/java/StevenDimDoors/experimental/DirectedGraph.java
Normal file
225
src/main/java/StevenDimDoors/experimental/DirectedGraph.java
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a complete implementation of a directed graph.
|
||||||
|
* @author SenseiKiwi
|
||||||
|
*
|
||||||
|
* @param <U> The type of data to store in the graph's nodes
|
||||||
|
* @param <V> The type of data to store in the graph's edges
|
||||||
|
*/
|
||||||
|
public class DirectedGraph<U, V>
|
||||||
|
{
|
||||||
|
private static class GraphNode<P, Q> implements IGraphNode<P, Q>
|
||||||
|
{
|
||||||
|
private LinkedList<Edge<P, Q>> inbound;
|
||||||
|
private LinkedList<Edge<P, Q>> outbound;
|
||||||
|
private ILinkedListNode<GraphNode<P, Q>> graphEntry;
|
||||||
|
private P data;
|
||||||
|
|
||||||
|
public GraphNode(P data, LinkedList<GraphNode<P, Q>> graphList)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
this.inbound = new LinkedList<Edge<P, Q>>();
|
||||||
|
this.outbound = new LinkedList<Edge<P, Q>>();
|
||||||
|
this.graphEntry = graphList.addLast(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int indegree()
|
||||||
|
{
|
||||||
|
return inbound.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int outdegree()
|
||||||
|
{
|
||||||
|
return outbound.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<Edge<P, Q>> inbound()
|
||||||
|
{
|
||||||
|
return inbound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<Edge<P, Q>> outbound()
|
||||||
|
{
|
||||||
|
return outbound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public P data()
|
||||||
|
{
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove()
|
||||||
|
{
|
||||||
|
graphEntry.remove();
|
||||||
|
graphEntry = null;
|
||||||
|
|
||||||
|
for (Edge<P, Q> edge : inbound)
|
||||||
|
edge.remove();
|
||||||
|
|
||||||
|
for (Edge<P, Q> edge : outbound)
|
||||||
|
edge.remove();
|
||||||
|
|
||||||
|
inbound = null;
|
||||||
|
outbound = null;
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Edge<P, Q> implements IEdge<P, Q>
|
||||||
|
{
|
||||||
|
private GraphNode<P, Q> head;
|
||||||
|
private GraphNode<P, Q> tail;
|
||||||
|
private ILinkedListNode<Edge<P, Q>> headEntry;
|
||||||
|
private ILinkedListNode<Edge<P, Q>> tailEntry;
|
||||||
|
private ILinkedListNode<Edge<P, Q>> graphEntry;
|
||||||
|
private Q data;
|
||||||
|
|
||||||
|
public Edge(GraphNode<P, Q> head, GraphNode<P, Q> tail, Q data, LinkedList<Edge<P, Q>> 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<P, Q> head()
|
||||||
|
{
|
||||||
|
return head;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IGraphNode<P, Q> 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<GraphNode<U, V>> nodes;
|
||||||
|
private LinkedList<Edge<U, V>> edges;
|
||||||
|
|
||||||
|
public DirectedGraph()
|
||||||
|
{
|
||||||
|
nodes = new LinkedList<GraphNode<U, V>>();
|
||||||
|
edges = new LinkedList<Edge<U, V>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int nodeCount()
|
||||||
|
{
|
||||||
|
return nodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int edgeCount()
|
||||||
|
{
|
||||||
|
return edges.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty()
|
||||||
|
{
|
||||||
|
return nodes.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<? extends IGraphNode<U, V>> nodes()
|
||||||
|
{
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterable<? extends IEdge<U, V>> edges()
|
||||||
|
{
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GraphNode<U, V> checkNode(IGraphNode<U, V> node)
|
||||||
|
{
|
||||||
|
GraphNode<U, V> innerNode = (GraphNode<U, V>) 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<U, V> checkEdge(IEdge<U, V> edge)
|
||||||
|
{
|
||||||
|
Edge<U, V> innerEdge = (Edge<U, V>) 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<U, V> addNode(U data)
|
||||||
|
{
|
||||||
|
return new GraphNode<U, V>(data, nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEdge<U, V> addEdge(IGraphNode<U, V> head, IGraphNode<U, V> tail, V data)
|
||||||
|
{
|
||||||
|
GraphNode<U, V> innerHead = checkNode(head);
|
||||||
|
GraphNode<U, V> innerTail = checkNode(tail);
|
||||||
|
return new Edge<U, V>(innerHead, innerTail, data, edges);
|
||||||
|
}
|
||||||
|
|
||||||
|
public U removeNode(IGraphNode<U, V> node)
|
||||||
|
{
|
||||||
|
GraphNode<U, V> innerNode = checkNode(node);
|
||||||
|
U data = innerNode.data();
|
||||||
|
innerNode.remove();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public V removeEdge(IEdge<U, V> edge)
|
||||||
|
{
|
||||||
|
Edge<U, V> innerEdge = checkEdge(edge);
|
||||||
|
V data = innerEdge.data();
|
||||||
|
innerEdge.remove();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEdge<U, V> findEdge(IGraphNode<U, V> head, IGraphNode<U, V> tail)
|
||||||
|
{
|
||||||
|
for (IEdge<U, V> 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<U, V> node : nodes)
|
||||||
|
{
|
||||||
|
node.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
134
src/main/java/StevenDimDoors/experimental/DisjointSet.java
Normal file
134
src/main/java/StevenDimDoors/experimental/DisjointSet.java
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class DisjointSet<T>
|
||||||
|
{
|
||||||
|
// This class implements a disjoint set data structure that associates objects with sets.
|
||||||
|
|
||||||
|
private static class SetNode<P>
|
||||||
|
{
|
||||||
|
private int rank;
|
||||||
|
private SetNode<P> parent;
|
||||||
|
private P data;
|
||||||
|
|
||||||
|
public SetNode(P data)
|
||||||
|
{
|
||||||
|
this.data = data;
|
||||||
|
this.rank = 0;
|
||||||
|
this.parent = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<T, SetNode<T>> mapping;
|
||||||
|
|
||||||
|
public DisjointSet(int initialCapacity)
|
||||||
|
{
|
||||||
|
mapping = new HashMap<T, SetNode<T>>(initialCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean makeSet(T element)
|
||||||
|
{
|
||||||
|
if (!mapping.containsKey(element))
|
||||||
|
{
|
||||||
|
mapping.put(element, new SetNode<T>(element));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SetNode<T> findRootNode(T element)
|
||||||
|
{
|
||||||
|
SetNode<T> 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<T> findRootNode(SetNode<T> node)
|
||||||
|
{
|
||||||
|
if (node.parent != null)
|
||||||
|
{
|
||||||
|
node.parent = findRootNode(node.parent);
|
||||||
|
return node.parent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean mergeSets(T first, T second)
|
||||||
|
{
|
||||||
|
SetNode<T> firstRoot = findRootNode(first);
|
||||||
|
SetNode<T> 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<T> root = findRootNode(element);
|
||||||
|
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
return root.data;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean haveSameSet(T first, T second)
|
||||||
|
{
|
||||||
|
SetNode<T> firstRoot = findRootNode(first);
|
||||||
|
SetNode<T> secondRoot = findRootNode(second);
|
||||||
|
|
||||||
|
if (firstRoot == null || secondRoot == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (firstRoot == secondRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
mapping.clear();
|
||||||
|
}
|
||||||
|
}
|
51
src/main/java/StevenDimDoors/experimental/DoorwayData.java
Normal file
51
src/main/java/StevenDimDoors/experimental/DoorwayData.java
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
8
src/main/java/StevenDimDoors/experimental/IEdge.java
Normal file
8
src/main/java/StevenDimDoors/experimental/IEdge.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
public interface IEdge<U, V>
|
||||||
|
{
|
||||||
|
public IGraphNode<U, V> head();
|
||||||
|
public IGraphNode<U, V> tail();
|
||||||
|
public V data();
|
||||||
|
}
|
10
src/main/java/StevenDimDoors/experimental/IGraphNode.java
Normal file
10
src/main/java/StevenDimDoors/experimental/IGraphNode.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
public interface IGraphNode<U, V>
|
||||||
|
{
|
||||||
|
public Iterable<? extends IEdge<U, V>> inbound();
|
||||||
|
public Iterable<? extends IEdge<U, V>> outbound();
|
||||||
|
public int indegree();
|
||||||
|
public int outdegree();
|
||||||
|
public U data();
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
public interface ILinkedListNode<T>
|
||||||
|
{
|
||||||
|
public ILinkedListNode<T> next();
|
||||||
|
public ILinkedListNode<T> prev();
|
||||||
|
public T data();
|
||||||
|
public void setData(T data);
|
||||||
|
public LinkedList<T> owner();
|
||||||
|
public T remove();
|
||||||
|
}
|
237
src/main/java/StevenDimDoors/experimental/LinkedList.java
Normal file
237
src/main/java/StevenDimDoors/experimental/LinkedList.java
Normal file
|
@ -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 <T> The type of data to be stored in the LinkedList
|
||||||
|
*/
|
||||||
|
public class LinkedList<T> implements Iterable<T>
|
||||||
|
{
|
||||||
|
private static class Node<P> implements ILinkedListNode<P>
|
||||||
|
{
|
||||||
|
private Node<P> next;
|
||||||
|
private Node<P> prev;
|
||||||
|
private P data;
|
||||||
|
private LinkedList<P> owner;
|
||||||
|
|
||||||
|
public Node(Node<P> prev, Node<P> next, P data, LinkedList<P> owner)
|
||||||
|
{
|
||||||
|
this.prev = prev;
|
||||||
|
this.next = next;
|
||||||
|
this.data = data;
|
||||||
|
this.owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ILinkedListNode<P> next()
|
||||||
|
{
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ILinkedListNode<P> 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<P> 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<P> implements Iterator<P>
|
||||||
|
{
|
||||||
|
private Node<P> current;
|
||||||
|
private Node<P> trailer;
|
||||||
|
|
||||||
|
public LinkedListIterator(LinkedList<P> 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<T> header; // Sentinel node
|
||||||
|
private Node<T> trailer; // Sentinel node
|
||||||
|
private int size;
|
||||||
|
|
||||||
|
public LinkedList()
|
||||||
|
{
|
||||||
|
size = 0;
|
||||||
|
header = new Node<T>(null, null, null, this);
|
||||||
|
trailer = new Node<T>(null, null, null, this);
|
||||||
|
header.next = trailer;
|
||||||
|
trailer.prev = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILinkedListNode<T> header()
|
||||||
|
{
|
||||||
|
return header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILinkedListNode<T> 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<T> current;
|
||||||
|
Node<T> next;
|
||||||
|
|
||||||
|
size = 0;
|
||||||
|
current = header.next;
|
||||||
|
while (current != trailer)
|
||||||
|
{
|
||||||
|
next = current.next;
|
||||||
|
current.clear();
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
header.next = trailer;
|
||||||
|
trailer.prev = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<T> checkNode(ILinkedListNode<T> node)
|
||||||
|
{
|
||||||
|
Node<T> innerNode = (Node<T>) 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<T> addFirst(T data)
|
||||||
|
{
|
||||||
|
return addAfter(header, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILinkedListNode<T> addLast(T data)
|
||||||
|
{
|
||||||
|
return addBefore(trailer, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILinkedListNode<T> addBefore(ILinkedListNode<T> 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<T> addAfter(ILinkedListNode<T> node, T data)
|
||||||
|
{
|
||||||
|
if (node == trailer)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Cannot add a node after the trailer node.");
|
||||||
|
}
|
||||||
|
return addAfter( checkNode(node), data );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node<T> addAfter(Node<T> node, T data)
|
||||||
|
{
|
||||||
|
Node<T> addition = new Node(node, node.next, data, this);
|
||||||
|
node.next = addition;
|
||||||
|
addition.next.prev = addition;
|
||||||
|
size++;
|
||||||
|
return addition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<T> iterator()
|
||||||
|
{
|
||||||
|
return new LinkedListIterator<T>(this);
|
||||||
|
}
|
||||||
|
}
|
232
src/main/java/StevenDimDoors/experimental/MazeBuilder.java
Normal file
232
src/main/java/StevenDimDoors/experimental/MazeBuilder.java
Normal file
|
@ -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<PartitionNode, DoorwayData> roomGraph, World world, Point3D offset)
|
||||||
|
{
|
||||||
|
for (IGraphNode<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
||||||
|
{
|
||||||
|
PartitionNode room = node.data();
|
||||||
|
buildBox(world, offset, room.minCorner(), room.maxCorner(), Block.stoneBrick.blockID, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void carveDoorways(DirectedGraph<PartitionNode, DoorwayData> roomGraph, World world,
|
||||||
|
Point3D offset, SphereDecayOperation decay, Random random)
|
||||||
|
{
|
||||||
|
char axis;
|
||||||
|
Point3D lower;
|
||||||
|
DoorwayData doorway;
|
||||||
|
|
||||||
|
for (IGraphNode<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
||||||
|
{
|
||||||
|
for (IEdge<PartitionNode, DoorwayData> 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);
|
||||||
|
}
|
||||||
|
}
|
48
src/main/java/StevenDimDoors/experimental/MazeDesign.java
Normal file
48
src/main/java/StevenDimDoors/experimental/MazeDesign.java
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package StevenDimDoors.experimental;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class MazeDesign
|
||||||
|
{
|
||||||
|
private PartitionNode root;
|
||||||
|
private DirectedGraph<PartitionNode, DoorwayData> rooms;
|
||||||
|
private ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores;
|
||||||
|
|
||||||
|
public MazeDesign(PartitionNode root, DirectedGraph<PartitionNode, DoorwayData> rooms,
|
||||||
|
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores)
|
||||||
|
{
|
||||||
|
this.root = root;
|
||||||
|
this.rooms = rooms;
|
||||||
|
this.cores = cores;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PartitionNode getRootPartition()
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DirectedGraph<PartitionNode, DoorwayData> getRoomGraph()
|
||||||
|
{
|
||||||
|
return rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArrayList<IGraphNode<PartitionNode, DoorwayData>> getCoreNodes()
|
||||||
|
{
|
||||||
|
return cores;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int width()
|
||||||
|
{
|
||||||
|
return root.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int height()
|
||||||
|
{
|
||||||
|
return root.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length()
|
||||||
|
{
|
||||||
|
return root.length();
|
||||||
|
}
|
||||||
|
}
|
579
src/main/java/StevenDimDoors/experimental/MazeDesigner.java
Normal file
579
src/main/java/StevenDimDoors/experimental/MazeDesigner.java
Normal file
|
@ -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<PartitionNode> partitions = new ArrayList<PartitionNode>(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<PartitionNode, DoorwayData> rooms = createRoomGraph(root, partitions, random);
|
||||||
|
|
||||||
|
// Cut out random subgraphs from the adjacency graph
|
||||||
|
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores = createMazeSections(rooms, random);
|
||||||
|
|
||||||
|
// Remove unnecessary passages through floors/ceilings and some from the walls
|
||||||
|
for (IGraphNode<PartitionNode, DoorwayData> core : cores)
|
||||||
|
{
|
||||||
|
pruneDoorways(core, rooms, random);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MazeDesign(root, rooms, cores);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void listRoomPartitions(PartitionNode node, ArrayList<PartitionNode> 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<PartitionNode, DoorwayData> createRoomGraph(PartitionNode root, ArrayList<PartitionNode> partitions, Random random)
|
||||||
|
{
|
||||||
|
DirectedGraph<PartitionNode, DoorwayData> roomGraph = new DirectedGraph<PartitionNode, DoorwayData>();
|
||||||
|
HashMap<PartitionNode, IGraphNode<PartitionNode, DoorwayData>> roomsToGraph = new HashMap<PartitionNode, IGraphNode<PartitionNode, DoorwayData>>(2 * partitions.size());
|
||||||
|
|
||||||
|
// Shuffle the list of rooms so that they're not listed in any ordered way in the room graph
|
||||||
|
// This is the only convenient way of randomizing the maze sections generated later
|
||||||
|
Collections.shuffle(partitions, random);
|
||||||
|
|
||||||
|
// Add all rooms to a graph
|
||||||
|
// 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<PartitionNode, DoorwayData> node : roomGraph.nodes())
|
||||||
|
{
|
||||||
|
findDoorways(node, root, roomsToGraph, roomGraph);
|
||||||
|
}
|
||||||
|
|
||||||
|
return roomGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void findDoorways(IGraphNode<PartitionNode, DoorwayData> roomNode, PartitionNode root,
|
||||||
|
HashMap<PartitionNode, IGraphNode<PartitionNode, DoorwayData>> roomsToGraph,
|
||||||
|
DirectedGraph<PartitionNode, DoorwayData> roomGraph)
|
||||||
|
{
|
||||||
|
// This function finds rooms adjacent to a specified room that could be connected
|
||||||
|
// 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<PartitionNode, DoorwayData> 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<IGraphNode<PartitionNode, DoorwayData>> createMazeSections(DirectedGraph<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> current;
|
||||||
|
IGraphNode<PartitionNode, DoorwayData> neighbor;
|
||||||
|
|
||||||
|
ArrayList<IGraphNode<PartitionNode, DoorwayData>> cores = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>();
|
||||||
|
ArrayList<IGraphNode<PartitionNode, DoorwayData>> removals = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>();
|
||||||
|
ArrayList<IGraphNode<PartitionNode, DoorwayData>> section = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>();
|
||||||
|
|
||||||
|
Queue<IGraphNode<PartitionNode, DoorwayData>> ordering = new LinkedList<IGraphNode<PartitionNode, DoorwayData>>();
|
||||||
|
HashMap<IGraphNode<PartitionNode, DoorwayData>, Integer> distances = new HashMap<IGraphNode<PartitionNode, DoorwayData>, Integer>();
|
||||||
|
|
||||||
|
// Repeatedly generate sections until all nodes have been visited
|
||||||
|
for (IGraphNode<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> edge : current.inbound())
|
||||||
|
{
|
||||||
|
neighbor = edge.head();
|
||||||
|
if (!distances.containsKey(neighbor))
|
||||||
|
{
|
||||||
|
distances.put(neighbor, distance);
|
||||||
|
ordering.add(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (IEdge<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> node : removals)
|
||||||
|
{
|
||||||
|
removeRoomPartitions(node.data());
|
||||||
|
roomGraph.removeNode(node);
|
||||||
|
}
|
||||||
|
return cores;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void pruneDoorways(IGraphNode<PartitionNode, DoorwayData> core,
|
||||||
|
DirectedGraph<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> current;
|
||||||
|
IGraphNode<PartitionNode, DoorwayData> neighbor;
|
||||||
|
|
||||||
|
Stack<IGraphNode<PartitionNode, DoorwayData>> ordering = new Stack<IGraphNode<PartitionNode, DoorwayData>>();
|
||||||
|
ArrayList<IGraphNode<PartitionNode, DoorwayData>> subgraph = new ArrayList<IGraphNode<PartitionNode, DoorwayData>>(64);
|
||||||
|
DisjointSet<IGraphNode<PartitionNode, DoorwayData>> components = new DisjointSet<IGraphNode<PartitionNode, DoorwayData>>(128);
|
||||||
|
|
||||||
|
ordering.add(core);
|
||||||
|
components.makeSet(core);
|
||||||
|
while (!ordering.isEmpty())
|
||||||
|
{
|
||||||
|
current = ordering.pop();
|
||||||
|
subgraph.add(current);
|
||||||
|
|
||||||
|
for (IEdge<PartitionNode, DoorwayData> edge : current.inbound())
|
||||||
|
{
|
||||||
|
neighbor = edge.head();
|
||||||
|
if (components.makeSet(neighbor))
|
||||||
|
{
|
||||||
|
ordering.add(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (IEdge<PartitionNode, DoorwayData> 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<IEdge<PartitionNode, DoorwayData>> targets =
|
||||||
|
new ArrayList<IEdge<PartitionNode, DoorwayData>>();
|
||||||
|
|
||||||
|
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
|
||||||
|
{
|
||||||
|
for (IEdge<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> room : subgraph)
|
||||||
|
{
|
||||||
|
components.makeSet(room);
|
||||||
|
}
|
||||||
|
for (IGraphNode<PartitionNode, DoorwayData> room : subgraph)
|
||||||
|
{
|
||||||
|
for (IEdge<PartitionNode, DoorwayData> 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<PartitionNode, DoorwayData> passage : targets)
|
||||||
|
{
|
||||||
|
if (!components.mergeSets(passage.head(), passage.tail()) && random.nextBoolean())
|
||||||
|
{
|
||||||
|
rooms.removeEdge(passage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
161
src/main/java/StevenDimDoors/experimental/PartitionNode.java
Normal file
161
src/main/java/StevenDimDoors/experimental/PartitionNode.java
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,10 +61,12 @@ public class Point3D implements Serializable {
|
||||||
{
|
{
|
||||||
return new Point3D(x, y, z);
|
return new Point3D(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] toIntArray()
|
public int[] toIntArray()
|
||||||
{
|
{
|
||||||
return new int[]{x,y,z};
|
return new int[]{x,y,z};
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Point3D other)
|
public boolean equals(Point3D other)
|
||||||
{
|
{
|
||||||
if (other == null)
|
if (other == null)
|
||||||
|
|
|
@ -25,7 +25,6 @@ import StevenDimDoors.mod_pocketDim.tileentities.TileEntityDimDoor;
|
||||||
import cpw.mods.fml.relauncher.Side;
|
import cpw.mods.fml.relauncher.Side;
|
||||||
import cpw.mods.fml.relauncher.SideOnly;
|
import cpw.mods.fml.relauncher.SideOnly;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public abstract class BaseDimDoor extends BlockDoor implements IDimDoor, ITileEntityProvider
|
public abstract class BaseDimDoor extends BlockDoor implements IDimDoor, ITileEntityProvider
|
||||||
{
|
{
|
||||||
protected final DDProperties properties;
|
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;
|
int direction = MathHelper.floor_double((entity.rotationYaw + 90) * 4.0F / 360.0F + 0.5D) & 3;
|
||||||
return ((metadata & 3) == direction);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -10,4 +10,6 @@ public interface IDimDoor
|
||||||
public void placeLink(World world, int x, int y, int z);
|
public void placeLink(World world, int x, int y, int z);
|
||||||
|
|
||||||
public int getDrops();
|
public int getDrops();
|
||||||
|
|
||||||
|
public void initDoorTE(World world, int x, int y, int z);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,4 +118,11 @@ public class TransTrapdoor extends BlockTrapDoor implements IDimDoor, ITileEntit
|
||||||
{
|
{
|
||||||
return (metadata & 8) == 0;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -300,7 +300,6 @@ public class PocketManager
|
||||||
PocketManager.dimensionData.put(dimData.id, dimData);
|
PocketManager.dimensionData.put(dimData.id, dimData);
|
||||||
dimWatcher.onCreated(new ClientDimData(dimData));
|
dimWatcher.onCreated(new ClientDimData(dimData));
|
||||||
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public static boolean deletePocket(NewDimData target, boolean deleteFolder)
|
public static boolean deletePocket(NewDimData target, boolean deleteFolder)
|
||||||
|
@ -403,7 +402,7 @@ public class PocketManager
|
||||||
*/
|
*/
|
||||||
private static void loadInternal()
|
private static void loadInternal()
|
||||||
{
|
{
|
||||||
System.out.println(!FMLCommonHandler.instance().getSide().isClient());
|
//System.out.println(!FMLCommonHandler.instance().getSide().isClient());
|
||||||
|
|
||||||
File saveDir = DimensionManager.getCurrentSaveRootDirectory();
|
File saveDir = DimensionManager.getCurrentSaveRootDirectory();
|
||||||
if (saveDir != null)
|
if (saveDir != null)
|
||||||
|
@ -507,14 +506,24 @@ public class PocketManager
|
||||||
DimensionManager.registerDimension(dimensionID, properties.PocketProviderID);
|
DimensionManager.registerDimension(dimensionID, properties.PocketProviderID);
|
||||||
return registerDimension(dimensionID, (InnerDimData) parent, true, isDungeon);
|
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)
|
private static NewDimData registerDimension(int dimensionID, InnerDimData parent, boolean isPocket, boolean isDungeon)
|
||||||
{
|
{
|
||||||
if (dimensionData.containsKey(dimensionID))
|
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.");
|
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);
|
InnerDimData dimension = new InnerDimData(dimensionID, parent, isPocket, isDungeon, linkWatcher);
|
||||||
dimensionData.put(dimensionID, dimension);
|
dimensionData.put(dimensionID, dimension);
|
||||||
if (!dimension.isPocketDimension())
|
if (!dimension.isPocketDimension())
|
||||||
|
@ -529,6 +538,7 @@ public class PocketManager
|
||||||
@SideOnly(Side.CLIENT)
|
@SideOnly(Side.CLIENT)
|
||||||
private static NewDimData registerClientDimension(int dimensionID, int rootID)
|
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
|
// 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
|
// getDimensionData() always handles root dimensions properly, even if the weren't defined before
|
||||||
|
|
||||||
|
@ -560,7 +570,7 @@ public class PocketManager
|
||||||
//Steven
|
//Steven
|
||||||
DimensionManager.registerDimension(dimensionID, mod_pocketDim.properties.PocketProviderID);
|
DimensionManager.registerDimension(dimensionID, mod_pocketDim.properties.PocketProviderID);
|
||||||
}
|
}
|
||||||
return dimension;
|
return dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NewDimData getDimensionData(World world)
|
public static NewDimData getDimensionData(World world)
|
||||||
|
@ -579,6 +589,7 @@ public class PocketManager
|
||||||
if(PocketManager.dimensionData == null)
|
if(PocketManager.dimensionData == null)
|
||||||
{
|
{
|
||||||
System.out.println("Something odd happend during shutdown");
|
System.out.println("Something odd happend during shutdown");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
NewDimData dimension = PocketManager.dimensionData.get(dimensionID);
|
NewDimData dimension = PocketManager.dimensionData.get(dimensionID);
|
||||||
if (dimension == null)
|
if (dimension == null)
|
||||||
|
|
|
@ -17,10 +17,13 @@ import net.minecraft.tileentity.TileEntity;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import StevenDimDoors.mod_pocketDim.DDProperties;
|
import StevenDimDoors.mod_pocketDim.DDProperties;
|
||||||
import StevenDimDoors.mod_pocketDim.Point3D;
|
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.DimLink;
|
||||||
import StevenDimDoors.mod_pocketDim.core.LinkTypes;
|
import StevenDimDoors.mod_pocketDim.core.LinkTypes;
|
||||||
import StevenDimDoors.mod_pocketDim.core.NewDimData;
|
import StevenDimDoors.mod_pocketDim.core.NewDimData;
|
||||||
import StevenDimDoors.mod_pocketDim.core.PocketManager;
|
import StevenDimDoors.mod_pocketDim.core.PocketManager;
|
||||||
|
import StevenDimDoors.mod_pocketDim.items.ItemDimensionalDoor;
|
||||||
import StevenDimDoors.mod_pocketDim.schematic.BlockRotator;
|
import StevenDimDoors.mod_pocketDim.schematic.BlockRotator;
|
||||||
import StevenDimDoors.mod_pocketDim.schematic.CompoundFilter;
|
import StevenDimDoors.mod_pocketDim.schematic.CompoundFilter;
|
||||||
import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException;
|
import StevenDimDoors.mod_pocketDim.schematic.InvalidSchematicException;
|
||||||
|
@ -293,6 +296,8 @@ public class DungeonSchematic extends Schematic {
|
||||||
Point4D destination = entryLink.source();
|
Point4D destination = entryLink.source();
|
||||||
NewDimData prevDim = PocketManager.getDimensionData(destination.getDimension());
|
NewDimData prevDim = PocketManager.getDimensionData(destination.getDimension());
|
||||||
prevDim.setDestination(reverseLink, destination.getX(), destination.getY(), destination.getZ());
|
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)
|
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);
|
int metadata = world.getBlockMetadata(x, y, z);
|
||||||
setBlockDirectly(world, x, y + 1, z, blockID, metadata);
|
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)
|
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());
|
int orientation = world.getBlockMetadata(location.getX(), location.getY()-1, location.getZ());
|
||||||
|
|
||||||
dimension.createLink(location.getX(), location.getY(), location.getZ(), LinkTypes.DUNGEON, orientation);
|
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)
|
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);
|
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!!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,15 +19,14 @@ public class TileEntityDimDoor extends TileEntity
|
||||||
@Override
|
@Override
|
||||||
public boolean canUpdate()
|
public boolean canUpdate()
|
||||||
{
|
{
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateEntity()
|
public void updateEntity()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
public Packet getDescriptionPacket()
|
public Packet getDescriptionPacket()
|
||||||
{
|
{
|
||||||
if(PocketManager.getLink(xCoord, yCoord, zCoord, worldObj)!=null)
|
if(PocketManager.getLink(xCoord, yCoord, zCoord, worldObj)!=null)
|
||||||
|
|
|
@ -8,6 +8,7 @@ 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 net.minecraftforge.common.DimensionManager;
|
import net.minecraftforge.common.DimensionManager;
|
||||||
|
import StevenDimDoors.experimental.MazeBuilder;
|
||||||
import StevenDimDoors.mod_pocketDim.DDProperties;
|
import StevenDimDoors.mod_pocketDim.DDProperties;
|
||||||
import StevenDimDoors.mod_pocketDim.Point3D;
|
import StevenDimDoors.mod_pocketDim.Point3D;
|
||||||
import StevenDimDoors.mod_pocketDim.blocks.IDimDoor;
|
import StevenDimDoors.mod_pocketDim.blocks.IDimDoor;
|
||||||
|
@ -471,6 +472,7 @@ public class PocketBuilder
|
||||||
Point3D door = new Point3D(x, y, z);
|
Point3D door = new Point3D(x, y, z);
|
||||||
BlockRotator.transformPoint(center, door, orientation - BlockRotator.EAST_DOOR_METADATA, door);
|
BlockRotator.transformPoint(center, door, orientation - BlockRotator.EAST_DOOR_METADATA, door);
|
||||||
|
|
||||||
|
|
||||||
//Build the outer layer of Eternal Fabric
|
//Build the outer layer of Eternal Fabric
|
||||||
buildBox(world, center.getX(), center.getY(), center.getZ(), (size / 2), properties.PermaFabricBlockID, false, 0);
|
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);
|
layer < (wallThickness - 1) && properties.TNFREAKINGT_Enabled, properties.NonTntWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//MazeBuilder.generate(world, x, y, z, random);
|
||||||
|
|
||||||
//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);
|
||||||
ItemDimensionalDoor.placeDoorBlock(world, x, y - 1, z, doorOrientation, doorBlock);
|
ItemDimensionalDoor.placeDoorBlock(world, x, y - 1, z, doorOrientation, doorBlock);
|
||||||
|
|
Loading…
Reference in a new issue