From 582db26cdc37f09e8e491d05383bd8d2ac7beb3f Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Thu, 3 Apr 2014 14:06:48 -0400 Subject: [PATCH 01/24] Fixed a crash in rift sig placement --- .../StevenDimDoors/mod_pocketDim/items/ItemRiftSignature.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/items/ItemRiftSignature.java b/src/main/java/StevenDimDoors/mod_pocketDim/items/ItemRiftSignature.java index 6f5c5b8..a20bc46 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/items/ItemRiftSignature.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/items/ItemRiftSignature.java @@ -142,6 +142,10 @@ public class ItemRiftSignature extends Item { y=y-2;//get the block the player actually clicked on Block block = Block.blocksList[world.getBlockId(x, y, z)]; + if(block==null) + { + return y+2; + } if(block.isBlockReplaceable(world, x, y, z)) { return y+1;//move block placement down (-2+1) one so its directly over things like snow -- 2.39.5 From 41fbcfe0ff3dc6e9be336d2bab2ec04b23856b11 Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Thu, 3 Apr 2014 14:09:36 -0400 Subject: [PATCH 02/24] Savedata backs up on write now --- .../mod_pocketDim/saving/DDSaveHandler.java | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java index b995edd..eed33b2 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -11,6 +11,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import org.apache.commons.io.FileUtils; import net.minecraftforge.common.DimensionManager; import StevenDimDoors.mod_pocketDim.Point3D; @@ -71,6 +72,10 @@ public class DDSaveHandler for (File dataFile : dataFiles) { PackedDimData packedDim = readDimension(dataFile, reader); + if(packedDim == null) + { + throw new IllegalStateException("The DD data for "+dataFile.getName().replace(".txt", "")+" at "+dataFile.getPath()+" is corrupted. Please report this on the MCF or on the DD github issues tracker."); + } packedDims.put(packedDim.ID,packedDim); } @@ -241,17 +246,24 @@ public class DDSaveHandler public static boolean saveAll(Iterable> dimensions, List blacklist) throws IOException { + long startTime = System.nanoTime(); + // Create the data directory for our dimensions // Don't catch exceptions here. If we can't create this folder, // the mod should crash to let the user know early on. // Get the save directory path File saveDirectory = new File(DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/data/"); + File backupDir = new File(saveDirectory+"/backup"); String savePath = saveDirectory.getAbsolutePath(); - - // Create the save directory - Files.createParentDirs(saveDirectory); - saveDirectory.mkdir(); + + + if(!saveDirectory.exists()) + { + // Create the save directory + Files.createParentDirs(saveDirectory); + saveDirectory.mkdir(); + } // Create and write the blackList writeBlacklist(blacklist, savePath); @@ -261,8 +273,11 @@ public class DDSaveHandler DimDataProcessor writer = new DimDataProcessor(); for (IPackable dimension : dimensions) { - succeeded &= writeDimension(dimension, writer, savePath + "/dim_"); + succeeded &= writeDimension(dimension, writer, savePath + "/dim_",backupDir); } + + startTime = System.nanoTime()-startTime; + System.out.println("Saving took "+startTime/1000000000D+" seconds"); return succeeded; } @@ -286,15 +301,20 @@ public class DDSaveHandler } } - private static boolean writeDimension(IPackable dimension, DimDataProcessor writer, String basePath) + private static boolean writeDimension(IPackable dimension, DimDataProcessor writer, String basePath, File backupDir) { try { - File tempFile = new File(basePath + (dimension.name() + ".tmp")); File saveFile = new File(basePath + (dimension.name() + ".txt")); - writer.writeToFile(tempFile, dimension.pack()); - saveFile.delete(); - tempFile.renameTo(saveFile); + + //If the savefile already exists, back it up. + if(saveFile.exists()) + { + FileUtils.copyFileToDirectory(saveFile, backupDir); + saveFile.delete(); + } + + writer.writeToFile(saveFile, dimension.pack()); return true; } catch (Exception e) -- 2.39.5 From aa7ee3c8fe161ff036ba114f2fb2f6495fd1e631 Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Thu, 3 Apr 2014 14:10:00 -0400 Subject: [PATCH 03/24] added logger --- .../StevenDimDoors/mod_pocketDim/util/Logger.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/StevenDimDoors/mod_pocketDim/util/Logger.java diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/util/Logger.java b/src/main/java/StevenDimDoors/mod_pocketDim/util/Logger.java new file mode 100644 index 0000000..c544f05 --- /dev/null +++ b/src/main/java/StevenDimDoors/mod_pocketDim/util/Logger.java @@ -0,0 +1,12 @@ +package StevenDimDoors.mod_pocketDim.util; + +public class Logger +{ + private static Logger instance; + + public Logger() + { + instance = this; + } + +} -- 2.39.5 From d98b6279de3bb28cad88a237dcc3cfc7d7dbc346 Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Thu, 3 Apr 2014 15:55:35 -0400 Subject: [PATCH 04/24] quick fix --- .../StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java | 2 +- .../java/StevenDimDoors/mod_pocketDim/util/DDLogger.java | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java index d8278be..2ae2a9a 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -46,7 +46,7 @@ public class DDSaveHandler // Don't surround this code with try-catch. Our mod should crash if an error // occurs at this level, since it could lead to some nasty problems. - DDLogger.logger().startTimer("Loading data"); + DDLogger.startTimer("Loading data"); String basePath = DimensionManager.getCurrentSaveRootDirectory() + "/DimensionalDoors/data/"; File dataDirectory = new File(basePath); diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java b/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java index 6a92967..1b28510 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java @@ -15,7 +15,7 @@ public class DDLogger this.log.append("Logger started.\n"); } - private static DDLogger logger() + public static DDLogger logger() { if( instance == null) { @@ -35,10 +35,9 @@ public class DDLogger { this.description=description; } - private DDTimer start() + private void start() { this.startTime=System.nanoTime(); - return this; } private void stop(long endTime) { -- 2.39.5 From 56e522a61abdc49a2f7a6407de2b45bd6ccb432b Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Thu, 3 Apr 2014 20:15:34 -0400 Subject: [PATCH 05/24] logging changes --- src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java | 2 ++ .../java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java b/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java index 529a032..54f22a9 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java @@ -315,6 +315,8 @@ public class mod_pocketDim { PocketManager.unload(); deathTracker.writeToFile(); + System.out.println(DDLogger.logger().printLog()); + DDLogger.logger().clearLog(); deathTracker = null; worldProperties = null; this.currrentSaveRootDirectory=null; diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java index 2ae2a9a..ef6e377 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -293,7 +293,6 @@ public class DDSaveHandler } DDLogger.stopTimer("Saving data"); - System.out.println(DDLogger.logger().printLog()); return succeeded; } -- 2.39.5 From d5e5e12cf920ddc9c742a57ddeea8dfbd38ee5e4 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Mon, 7 Apr 2014 09:25:20 -0400 Subject: [PATCH 06/24] Refactored Maze Generation to use RoomData Rewrote portions of our maze generation code to use RoomData. This provides an object that unifies all room data instead of having it spread across various data structures and linked loosely by hash maps. We'll need this to implement the remaining generation features. --- .../experimental/MazeBuilder.java | 17 +- .../experimental/MazeDesign.java | 24 +-- .../experimental/MazeDesigner.java | 202 +++++++++--------- .../experimental/MazeLinkData.java | 6 + .../experimental/PartitionNode.java | 13 +- .../StevenDimDoors/experimental/RoomData.java | 75 +++++++ 6 files changed, 206 insertions(+), 131 deletions(-) create mode 100644 src/main/java/StevenDimDoors/experimental/MazeLinkData.java create mode 100644 src/main/java/StevenDimDoors/experimental/RoomData.java diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index 9e31e9a..00348c1 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -18,8 +18,8 @@ public class MazeBuilder 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); + buildRooms(design.getLayout(), world, offset); + carveDoorways(design.getLayout(), world, offset, decay, random); //placeDoors(design, world, offset); @@ -32,25 +32,25 @@ public class MazeBuilder //final int DECAY_BOX_SIZE = 8 } - private static void buildRooms(DirectedGraph roomGraph, World world, Point3D offset) + private static void buildRooms(DirectedGraph layout, World world, Point3D offset) { - for (IGraphNode node : roomGraph.nodes()) + for (IGraphNode node : layout.nodes()) { - PartitionNode room = node.data(); + PartitionNode room = node.data().getPartitionNode(); buildBox(world, offset, room.minCorner(), room.maxCorner(), Block.stoneBrick.blockID, 0); } } - private static void carveDoorways(DirectedGraph roomGraph, World world, + private static void carveDoorways(DirectedGraph layout, World world, Point3D offset, SphereDecayOperation decay, Random random) { char axis; Point3D lower; DoorwayData doorway; - for (IGraphNode node : roomGraph.nodes()) + for (IGraphNode node : layout.nodes()) { - for (IEdge passage : node.outbound()) + for (IEdge passage : node.outbound()) { doorway = passage.data(); axis = doorway.axis(); @@ -165,7 +165,6 @@ public class MazeBuilder 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) { diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesign.java b/src/main/java/StevenDimDoors/experimental/MazeDesign.java index e7d4b4c..c88f1cd 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesign.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesign.java @@ -5,16 +5,12 @@ import java.util.ArrayList; public class MazeDesign { private PartitionNode root; - private DirectedGraph rooms; - private ArrayList> cores; - private ArrayList protectedAreas; + private DirectedGraph layout; - public MazeDesign(PartitionNode root, DirectedGraph rooms, - ArrayList> cores) + public MazeDesign(PartitionNode root, DirectedGraph layout) { this.root = root; - this.rooms = rooms; - this.cores = cores; + this.layout = layout; } public PartitionNode getRootPartition() @@ -22,19 +18,9 @@ public class MazeDesign return root; } - public DirectedGraph getRoomGraph() + public DirectedGraph getLayout() { - return rooms; - } - - public ArrayList> getCoreNodes() - { - return cores; - } - - public ArrayList getProtectedAreas() - { - return protectedAreas; + return layout; } public int width() diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java index 289931a..e9d7c31 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java @@ -25,55 +25,66 @@ public class 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); + 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); + // Attach rooms to all the leaf nodes of the partition tree + ArrayList rooms = new ArrayList(1 << SPLIT_COUNT); + attachRooms(root, rooms); + + // 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(rooms, random); // 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); + DirectedGraph layout = createRoomGraph(root, rooms, random); // Cut out random subgraphs from the adjacency graph - ArrayList> cores = createMazeSections(rooms, random); + ArrayList cores = createMazeSections(layout, random); // Remove unnecessary passages through floors/ceilings and some from the walls - for (IGraphNode core : cores) + for (RoomData core : cores) { - pruneDoorways(core, rooms, random); + pruneDoorways(core.getLayoutNode(), layout, random); } - return new MazeDesign(root, rooms, cores); + return new MazeDesign(root, layout); } - private static void listRoomPartitions(PartitionNode node, ArrayList partitions) + private static void attachRooms(PartitionNode node, ArrayList partitions) { if (node.isLeaf()) { - partitions.add(node); + partitions.add(new RoomData(node)); } else { - listRoomPartitions(node.leftChild(), partitions); - listRoomPartitions(node.rightChild(), partitions); + attachRooms(node.leftChild(), partitions); + attachRooms(node.rightChild(), partitions); } } - private static void removeRoomPartitions(PartitionNode node) + private static void removeRoom(RoomData room, DirectedGraph layout) { - // Remove a node and any of its ancestors that become leaf nodes + // Remove the room from the partition tree and from the layout graph. + // Also remove any ancestors that become leaf nodes. PartitionNode parent; PartitionNode current; - current = node; + current = room.getPartitionNode(); while (current != null && current.isLeaf()) { parent = current.parent(); current.remove(); current = parent; } + + // Remove the room from the layout graph + layout.removeNode(room.getLayoutNode()); + + // Wipe the room's data, as a precaution. + room.clear(); } private static PartitionNode partitionRooms(int width, int height, int length, int maxLevels, Random random) @@ -140,35 +151,25 @@ public class MazeDesigner } } - private static DirectedGraph createRoomGraph(PartitionNode root, ArrayList partitions, Random random) + private static DirectedGraph createRoomGraph(PartitionNode root, ArrayList rooms, 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); + DirectedGraph layout = new DirectedGraph(); // 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) + for (RoomData room : rooms) { - roomsToGraph.put(partition, roomGraph.addNode(partition)); + room.addToLayout(layout); } - // Add edges for each room - for (IGraphNode node : roomGraph.nodes()) + for (IGraphNode node : layout.nodes()) { - findDoorways(node, root, roomsToGraph, roomGraph); + findDoorways(node.data(), root, layout); } - - return roomGraph; + return layout; } - private static void findDoorways(IGraphNode roomNode, PartitionNode root, - HashMap> roomsToGraph, - DirectedGraph roomGraph) + private static void findDoorways(RoomData room, PartitionNode root, + DirectedGraph layout) { // 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 @@ -186,7 +187,7 @@ public class MazeDesigner // there will always be a way to walk from any room to any other room. boolean[][] detected; - PartitionNode adjacent; + PartitionNode adjacent; int a, b, c; int p, q, r; @@ -195,11 +196,10 @@ public class MazeDesigner Point3D otherMin; Point3D otherMax; DoorwayData doorway; - IGraphNode adjacentNode; - PartitionNode room = roomNode.data(); - Point3D minCorner = room.minCorner(); - Point3D maxCorner = room.maxCorner(); + PartitionNode partition = room.getPartitionNode(); + Point3D minCorner = partition.minCorner(); + Point3D maxCorner = partition.maxCorner(); int minX = minCorner.getX(); int minY = minCorner.getY(); @@ -209,9 +209,9 @@ public class MazeDesigner int maxY = maxCorner.getY(); int maxZ = maxCorner.getZ(); - int width = room.width(); - int height = room.height(); - int length = room.length(); + int width = partition.width(); + int height = partition.height(); + int length = partition.length(); if (maxZ < root.maxCorner().getZ()) { @@ -247,8 +247,7 @@ public class MazeDesigner 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); + layout.addEdge(room.getLayoutNode(), adjacent.getData().getLayoutNode(), doorway); } } else @@ -295,8 +294,7 @@ public class MazeDesigner 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); + layout.addEdge(room.getLayoutNode(), adjacent.getData().getLayoutNode(), doorway); } } else @@ -343,8 +341,7 @@ public class MazeDesigner 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); + layout.addEdge(room.getLayoutNode(), adjacent.getData().getLayoutNode(), doorway); } } else @@ -359,7 +356,7 @@ public class MazeDesigner //Done! } - private static ArrayList> createMazeSections(DirectedGraph roomGraph, Random random) + private static ArrayList createMazeSections(DirectedGraph layout, Random random) { // The randomness of the sections generated here hinges on // the nodes in the graph being in a random order. We assume @@ -369,66 +366,68 @@ public class MazeDesigner final int MIN_SECTION_ROOMS = 5; int distance; - IGraphNode current; - IGraphNode neighbor; + RoomData room; + RoomData neighbor; + IGraphNode current; - ArrayList> cores = new ArrayList>(); - ArrayList> removals = new ArrayList>(); - ArrayList> section = new ArrayList>(); + ArrayList cores = new ArrayList(); + ArrayList removals = new ArrayList(); + ArrayList section = new ArrayList(); - Queue> ordering = new LinkedList>(); - HashMap, Integer> distances = new HashMap, Integer>(); + Queue ordering = new LinkedList(); // Repeatedly generate sections until all nodes have been visited - for (IGraphNode node : roomGraph.nodes()) + for (IGraphNode node : layout.nodes()) { - // If this node hasn't been visited, then use it as the core of a new section + // If this room hasn't been visited (distance = -1), then use it as the core of a new section // Otherwise, ignore it, since it was already processed - if (!distances.containsKey(node)) + room = node.data(); + if (room.getDistance() < 0) { // Perform a breadth-first search to tag surrounding nodes with distances - distances.put(node, 0); - ordering.add(node); + room.setDistance(0); + ordering.add(room); section.clear(); while (!ordering.isEmpty()) { - current = ordering.remove(); - distance = distances.get(current) + 1; + room = ordering.remove(); + distance = room.getDistance() + 1; if (distance <= MAX_DISTANCE + 1) { - section.add(current); + section.add(room); + current = room.getLayoutNode(); - // Visit neighboring nodes and assign them distances, if they don't - // have a distance assigned already - for (IEdge edge : current.inbound()) + // Visit neighboring rooms and assign them distances, if they don't + // have a proper distance assigned already + for (IEdge edge : current.inbound()) { - neighbor = edge.head(); - if (!distances.containsKey(neighbor)) + neighbor = edge.head().data(); + if (neighbor.getDistance() < 0) { - distances.put(neighbor, distance); + neighbor.setDistance(distance); ordering.add(neighbor); } } - for (IEdge edge : current.outbound()) + for (IEdge edge : current.outbound()) { - neighbor = edge.tail(); - if (!distances.containsKey(neighbor)) + neighbor = edge.tail().data(); + if (neighbor.getDistance() < 0) { - distances.put(neighbor, distance); + neighbor.setDistance(distance); ordering.add(neighbor); } } } else { - removals.add(current); + removals.add(room); break; } } - // List nodes that have a distance of exactly MAX_DISTANCE + 1 + // List rooms 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. @@ -440,7 +439,7 @@ public class MazeDesigner // Check if this section contains enough rooms if (section.size() >= MIN_SECTION_ROOMS) { - cores.add(node); + cores.add(node.data()); } else { @@ -449,18 +448,17 @@ public class MazeDesigner } } - // Remove all the nodes that were listed for removal + // Remove all the rooms that were listed for removal // Also remove unused partitions from the partition tree - for (IGraphNode node : removals) + for (RoomData target : removals) { - removeRoomPartitions(node.data()); - roomGraph.removeNode(node); + removeRoom(target, layout); } return cores; } - private static void pruneDoorways(IGraphNode core, - DirectedGraph rooms, Random random) + private static void pruneDoorways(IGraphNode core, + DirectedGraph layout, 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 @@ -478,12 +476,12 @@ public class MazeDesigner // idea applies for the other doorways, plus some randomness. // First, list all nodes in the subgraph - IGraphNode current; - IGraphNode neighbor; + IGraphNode current; + IGraphNode neighbor; - Stack> ordering = new Stack>(); - ArrayList> subgraph = new ArrayList>(64); - DisjointSet> components = new DisjointSet>(128); + Stack> ordering = new Stack>(); + ArrayList> subgraph = new ArrayList>(64); + DisjointSet> components = new DisjointSet>(128); ordering.add(core); components.makeSet(core); @@ -492,7 +490,7 @@ public class MazeDesigner current = ordering.pop(); subgraph.add(current); - for (IEdge edge : current.inbound()) + for (IEdge edge : current.inbound()) { neighbor = edge.head(); if (components.makeSet(neighbor)) @@ -500,7 +498,7 @@ public class MazeDesigner ordering.add(neighbor); } } - for (IEdge edge : current.outbound()) + for (IEdge edge : current.outbound()) { neighbor = edge.tail(); if (components.makeSet(neighbor)) @@ -513,12 +511,12 @@ public class MazeDesigner // 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>(); + ArrayList> targets = + new ArrayList>(); - for (IGraphNode room : subgraph) + for (IGraphNode room : subgraph) { - for (IEdge passage : room.outbound()) + for (IEdge passage : room.outbound()) { if (passage.data().axis() != DoorwayData.Y_AXIS) { @@ -535,11 +533,11 @@ public class MazeDesigner Collections.shuffle(targets, random); // Merge sets together and remove unnecessary doorways - for (IEdge passage : targets) + for (IEdge passage : targets) { if (!components.mergeSets(passage.head(), passage.tail())) { - rooms.removeEdge(passage); + layout.removeEdge(passage); } } @@ -548,13 +546,13 @@ public class MazeDesigner components.clear(); targets.clear(); - for (IGraphNode room : subgraph) + for (IGraphNode room : subgraph) { components.makeSet(room); } - for (IGraphNode room : subgraph) + for (IGraphNode room : subgraph) { - for (IEdge passage : room.outbound()) + for (IEdge passage : room.outbound()) { if (passage.data().axis() == DoorwayData.Y_AXIS) { @@ -567,11 +565,11 @@ public class MazeDesigner } } Collections.shuffle(targets, random); - for (IEdge passage : targets) + for (IEdge passage : targets) { if (!components.mergeSets(passage.head(), passage.tail()) && random.nextBoolean()) { - rooms.removeEdge(passage); + layout.removeEdge(passage); } } } diff --git a/src/main/java/StevenDimDoors/experimental/MazeLinkData.java b/src/main/java/StevenDimDoors/experimental/MazeLinkData.java new file mode 100644 index 0000000..efff141 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/MazeLinkData.java @@ -0,0 +1,6 @@ +package StevenDimDoors.experimental; + +public class MazeLinkData +{ + +} diff --git a/src/main/java/StevenDimDoors/experimental/PartitionNode.java b/src/main/java/StevenDimDoors/experimental/PartitionNode.java index df53854..5226378 100644 --- a/src/main/java/StevenDimDoors/experimental/PartitionNode.java +++ b/src/main/java/StevenDimDoors/experimental/PartitionNode.java @@ -2,11 +2,12 @@ package StevenDimDoors.experimental; import StevenDimDoors.mod_pocketDim.Point3D; -public class PartitionNode extends BoundingBox +public class PartitionNode extends BoundingBox { private PartitionNode parent; private PartitionNode leftChild = null; private PartitionNode rightChild = null; + private T data = null; public PartitionNode(int width, int height, int length) { @@ -122,4 +123,14 @@ public class PartitionNode extends BoundingBox return this; } } + + public void setData(T value) + { + this.data = value; + } + + public T getData() + { + return data; + } } diff --git a/src/main/java/StevenDimDoors/experimental/RoomData.java b/src/main/java/StevenDimDoors/experimental/RoomData.java new file mode 100644 index 0000000..eacfcaf --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/RoomData.java @@ -0,0 +1,75 @@ +package StevenDimDoors.experimental; + +import java.util.ArrayList; + +public class RoomData +{ + private int distance; + private boolean decayed; + private PartitionNode partitionNode; + private ArrayList inboundLinks; + private ArrayList outboundLinks; + private IGraphNode layoutNode; + + public RoomData(PartitionNode partitionNode) + { + this.partitionNode = partitionNode; + this.inboundLinks = new ArrayList(); + this.outboundLinks = new ArrayList(); + this.layoutNode = null; + this.distance = -1; + this.decayed = false; + partitionNode.setData(this); + } + + public PartitionNode getPartitionNode() + { + return this.partitionNode; + } + + public IGraphNode getLayoutNode() + { + return this.layoutNode; + } + + public void addToLayout(DirectedGraph layout) + { + this.layoutNode = layout.addNode(this); + } + + public boolean isDecayed() + { + return decayed; + } + + public void setDecayed(boolean value) + { + this.decayed = value; + } + + public ArrayList getInboundLinks() + { + return this.inboundLinks; + } + + public ArrayList getOutboundLinks() + { + return this.outboundLinks; + } + + public int getDistance() + { + return distance; + } + + public void setDistance(int value) + { + distance = value; + } + + public void clear() + { + partitionNode = null; + layoutNode = null; + } +} -- 2.39.5 From 5210de2e71740aafdd7c52e2ad69e2f588f16cc7 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Mon, 7 Apr 2014 09:39:10 -0400 Subject: [PATCH 07/24] Hacked PocketBuilder to Generate Mazes Made a minor change to PocketBuilder so that mazes generate instead of regular pocket dimensions. I'm only doing this to test dungeon generation - it'll get switched back once mazes are ready. --- .../StevenDimDoors/mod_pocketDim/world/PocketBuilder.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java b/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java index f26939b..913c282 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java @@ -465,6 +465,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); @@ -474,8 +475,9 @@ public class PocketBuilder buildBox(world, center.getX(), center.getY(), center.getZ(), (size / 2) - layer, properties.FabricBlockID, layer < (wallThickness - 1) && properties.TNFREAKINGT_Enabled, properties.NonTntWeight); } + */ - //MazeBuilder.generate(world, x, y, z, random); + 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); -- 2.39.5 From 935070e4367a2f68eb5c37cf11856002ff4c0990 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Tue, 8 Apr 2014 06:34:45 -0400 Subject: [PATCH 08/24] Minor Change Minor correction to a comment in DirectedGraph --- src/main/java/StevenDimDoors/experimental/DirectedGraph.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/StevenDimDoors/experimental/DirectedGraph.java b/src/main/java/StevenDimDoors/experimental/DirectedGraph.java index cd56c75..df2aa6c 100644 --- a/src/main/java/StevenDimDoors/experimental/DirectedGraph.java +++ b/src/main/java/StevenDimDoors/experimental/DirectedGraph.java @@ -165,8 +165,8 @@ public class DirectedGraph { Edge innerEdge = (Edge) edge; - // Check that this node actually belongs to this graph instance. - // Accepting foreign nodes could corrupt the graph's internal state. + // Check that this edge actually belongs to this graph instance. + // Accepting foreign edges could corrupt the graph's internal state. if (innerEdge.graphEntry.owner() != edges) { throw new IllegalArgumentException("The specified edge does not belong to this graph."); -- 2.39.5 From f92020323f4685c04db4bdc915c23bc3c1a3ff63 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Tue, 8 Apr 2014 06:40:45 -0400 Subject: [PATCH 09/24] Partial Implementation of Doors in Mazes Started implementing the placement of Dimensional Doors in mazes. Currently, a design is guaranteed to have enough space for some doors. MazeDesigner still needs more code to plan out which rooms will have doors and where those doors will lead. --- .../experimental/MazeDesigner.java | 99 +++++++++++-------- .../StevenDimDoors/experimental/RoomData.java | 7 ++ 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java index e9d7c31..ff01d9f 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java @@ -2,7 +2,6 @@ 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; @@ -362,13 +361,23 @@ public class MazeDesigner // the nodes in the graph being in a random order. We assume // that was handled in a previous step! + // We split the maze into sections by choosing core rooms and removing + // rooms that are a certain number of doorways away. However, for a section + // to be valid, it must also have enough space for at least two doors in + // rooms without floor holes. If a section can't fit two doors, more + // neighboring rooms are added until the necessary space is found or the + // search space is exhausted. + final int MAX_DISTANCE = 2; final int MIN_SECTION_ROOMS = 5; + final int MIN_SECTION_CAPACITY = 2; int distance; + int capacity; RoomData room; RoomData neighbor; - IGraphNode current; + boolean hasHoles; + IGraphNode roomNode; ArrayList cores = new ArrayList(); ArrayList removals = new ArrayList(); @@ -385,59 +394,67 @@ public class MazeDesigner if (room.getDistance() < 0) { // Perform a breadth-first search to tag surrounding nodes with distances - room.setDistance(0); ordering.add(room); + room.setDistance(0); section.clear(); + capacity = 0; - while (!ordering.isEmpty()) + while (room != null && (room.getDistance() <= MAX_DISTANCE || capacity < 2)) { - room = ordering.remove(); + ordering.remove(); + section.add(room); + roomNode = room.getLayoutNode(); distance = room.getDistance() + 1; + hasHoles = false; - if (distance <= MAX_DISTANCE + 1) + // Visit neighboring rooms and assign them distances, + // if they don't have a proper distance assigned already. + // Also check for floor holes. + for (IEdge edge : roomNode.inbound()) { - section.add(room); - current = room.getLayoutNode(); - - // Visit neighboring rooms and assign them distances, if they don't - // have a proper distance assigned already - for (IEdge edge : current.inbound()) + neighbor = edge.head().data(); + if (neighbor.getDistance() < 0) { - neighbor = edge.head().data(); - if (neighbor.getDistance() < 0) - { - neighbor.setDistance(distance); - ordering.add(neighbor); - } + neighbor.setDistance(distance); + ordering.add(neighbor); } - for (IEdge edge : current.outbound()) + if (edge.data().axis() == DoorwayData.Y_AXIS) { - neighbor = edge.tail().data(); - if (neighbor.getDistance() < 0) - { - neighbor.setDistance(distance); - ordering.add(neighbor); - } + hasHoles = true; } } - else + for (IEdge edge : roomNode.outbound()) { - removals.add(room); - break; + neighbor = edge.tail().data(); + if (neighbor.getDistance() < 0) + { + neighbor.setDistance(distance); + ordering.add(neighbor); + } } + + // Count this room's door capacity if it has no floor holes + if (!hasHoles) + { + capacity += room.estimateDoorCapacity(); + } + + room = ordering.peek(); } - // List rooms 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()) + // The remaining rooms in the ordering are those that are at the + // frontier of structure. They must be removed to create a gap + // between this section and other sections. But we can't remove + // the rooms immediately because that could break the iterator + // for the graph. + if (!ordering.isEmpty()) { - removals.add(ordering.remove()); + removals.addAll(ordering); + ordering.clear(); } - // Check if this section contains enough rooms - if (section.size() >= MIN_SECTION_ROOMS) + // Check if this section contains enough rooms and capacity for doors + if (section.size() >= MIN_SECTION_ROOMS && capacity >= MIN_SECTION_CAPACITY) { cores.add(node.data()); } @@ -449,7 +466,6 @@ public class MazeDesigner } // Remove all the rooms that were listed for removal - // Also remove unused partitions from the partition tree for (RoomData target : removals) { removeRoom(target, layout); @@ -508,9 +524,12 @@ public class MazeDesigner } } - // 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 + // Now iterate over the list of nodes and merge their sets based on + // being connected by X_AXIS or Z_AXIS doorways. We only have to look + // at outbound edges since inbound edges mirror them. List any Y_AXIS + // doorways we come across to consider removing them later, depending + // on their impact on connectedness. + // doorways. ArrayList> targets = new ArrayList>(); diff --git a/src/main/java/StevenDimDoors/experimental/RoomData.java b/src/main/java/StevenDimDoors/experimental/RoomData.java index eacfcaf..0334d33 100644 --- a/src/main/java/StevenDimDoors/experimental/RoomData.java +++ b/src/main/java/StevenDimDoors/experimental/RoomData.java @@ -72,4 +72,11 @@ public class RoomData partitionNode = null; layoutNode = null; } + + public int estimateDoorCapacity() + { + int cellsX = (partitionNode.width() - 3) / 2; + int cellsZ = (partitionNode.length() - 3) / 2; + return Math.min(cellsX * cellsZ, 3); + } } -- 2.39.5 From 18460348af1974335bea90f1f318f40e6c6c96c8 Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Fri, 11 Apr 2014 19:27:26 -0400 Subject: [PATCH 10/24] stopped logging --- src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java | 2 -- .../java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java b/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java index 54f22a9..529a032 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java @@ -315,8 +315,6 @@ public class mod_pocketDim { PocketManager.unload(); deathTracker.writeToFile(); - System.out.println(DDLogger.logger().printLog()); - DDLogger.logger().clearLog(); deathTracker = null; worldProperties = null; this.currrentSaveRootDirectory=null; diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java index ef6e377..a40e293 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -252,7 +252,6 @@ public class DDSaveHandler public static boolean saveAll(Iterable> dimensions, List blacklist, boolean checkModified) throws IOException { - DDLogger.startTimer("Saving data"); // Create the data directory for our dimensions // Don't catch exceptions here. If we can't create this folder, // the mod should crash to let the user know early on. @@ -292,7 +291,6 @@ public class DDSaveHandler } } - DDLogger.stopTimer("Saving data"); return succeeded; } -- 2.39.5 From 53b5591149f1883291c57f08a221d75493430da4 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Fri, 11 Apr 2014 19:28:05 -0400 Subject: [PATCH 11/24] Partial Implementation of Doors in Mazes Made progress on implementing the placement of doors in mazes. Still incomplete. --- .../StevenDimDoors/experimental/LinkPlan.java | 87 ++++++++++++ .../experimental/MazeBuilder.java | 20 ++- .../experimental/MazeDesigner.java | 128 ++++++++++++++---- .../experimental/MazeLinkData.java | 6 - .../StevenDimDoors/experimental/RoomData.java | 69 ++++++++-- 5 files changed, 270 insertions(+), 40 deletions(-) create mode 100644 src/main/java/StevenDimDoors/experimental/LinkPlan.java delete mode 100644 src/main/java/StevenDimDoors/experimental/MazeLinkData.java diff --git a/src/main/java/StevenDimDoors/experimental/LinkPlan.java b/src/main/java/StevenDimDoors/experimental/LinkPlan.java new file mode 100644 index 0000000..3fe6f75 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/LinkPlan.java @@ -0,0 +1,87 @@ +package StevenDimDoors.experimental; + +public class LinkPlan +{ + private RoomData source; + private RoomData destination; + private boolean entrance; + + private LinkPlan(RoomData source, RoomData destination, boolean entrance) + { + this.source = source; + this.destination = destination; + this.entrance = entrance; + } + + public static LinkPlan createInternalLink(RoomData source, RoomData destination) + { + if (source == null) + { + throw new IllegalArgumentException("source cannot be null."); + } + if (destination == null) + { + throw new IllegalArgumentException("destination cannot be null."); + } + LinkPlan plan = new LinkPlan(source, destination, false); + source.getOutboundLinks().add(plan); + destination.getInboundLinks().add(plan); + return plan; + } + + public static LinkPlan createEntranceLink(RoomData source) + { + if (source == null) + { + throw new IllegalArgumentException("source cannot be null."); + } + LinkPlan plan = new LinkPlan(source, null, true); + source.getOutboundLinks().add(plan); + return plan; + } + + public static LinkPlan createDungeonLink(RoomData source) + { + if (source == null) + { + throw new IllegalArgumentException("source cannot be null."); + } + LinkPlan plan = new LinkPlan(source, null, false); + source.getOutboundLinks().add(plan); + return plan; + } + + public RoomData source() + { + return this.source; + } + + public RoomData destination() + { + return this.destination; + } + + public boolean isEntrance() + { + return entrance; + } + + public boolean isInternal() + { + return (destination != null); + } + + public void remove() + { + if (source != null) + { + source.getOutboundLinks().remove(this); + source = null; + } + if (destination != null) + { + destination.getInboundLinks().remove(this); + destination = null; + } + } +} diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index 00348c1..6505f99 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -14,6 +14,8 @@ public class MazeBuilder public static void generate(World world, int x, int y, int z, Random random) { + // ISSUE FOR LATER: The room needs to be shifted so as to be centered on its entrance + MazeDesign design = MazeDesigner.generate(random); Point3D offset = new Point3D(x - design.width() / 2, y - design.height() - 1, z - design.length() / 2); SphereDecayOperation decay = new SphereDecayOperation(random, 0, 0, Block.stoneBrick.blockID, 2); @@ -21,7 +23,7 @@ public class MazeBuilder buildRooms(design.getLayout(), world, offset); carveDoorways(design.getLayout(), world, offset, decay, random); - //placeDoors(design, world, offset); + placeDoors(design.getLayout(), world, offset); applyRandomDestruction(design, world, offset, decay, random); } @@ -41,6 +43,22 @@ public class MazeBuilder } } + private static void placeDoors(DirectedGraph layout, World world, Point3D offset) + { + for (IGraphNode node : layout.nodes()) + { + RoomData room = node.data(); + Point3D minCorner = room.getPartitionNode().minCorner(); + if (!room.getOutboundLinks().isEmpty()) + { + setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 1, + offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0); + setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 2, + offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0); + } + } + } + private static void carveDoorways(DirectedGraph layout, World world, Point3D offset, SphereDecayOperation decay, Random random) { diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java index ff01d9f..0026303 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java @@ -1,7 +1,9 @@ package StevenDimDoors.experimental; +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Random; @@ -48,6 +50,9 @@ public class MazeDesigner pruneDoorways(core.getLayoutNode(), layout, random); } + // Set up the placement of dimensional doors within the maze + createMazeLinks(layout, cores, random); + return new MazeDesign(root, layout); } @@ -64,28 +69,6 @@ public class MazeDesigner } } - private static void removeRoom(RoomData room, DirectedGraph layout) - { - // Remove the room from the partition tree and from the layout graph. - // Also remove any ancestors that become leaf nodes. - PartitionNode parent; - PartitionNode current; - - current = room.getPartitionNode(); - while (current != null && current.isLeaf()) - { - parent = current.parent(); - current.remove(); - current = parent; - } - - // Remove the room from the layout graph - layout.removeNode(room.getLayoutNode()); - - // Wipe the room's data, as a precaution. - room.clear(); - } - private static PartitionNode partitionRooms(int width, int height, int length, int maxLevels, Random random) { PartitionNode root = new PartitionNode(width, height, length); @@ -468,7 +451,7 @@ public class MazeDesigner // Remove all the rooms that were listed for removal for (RoomData target : removals) { - removeRoom(target, layout); + target.remove(); } return cores; } @@ -529,7 +512,6 @@ public class MazeDesigner // at outbound edges since inbound edges mirror them. List any Y_AXIS // doorways we come across to consider removing them later, depending // on their impact on connectedness. - // doorways. ArrayList> targets = new ArrayList>(); @@ -557,6 +539,7 @@ public class MazeDesigner if (!components.mergeSets(passage.head(), passage.tail())) { layout.removeEdge(passage); + } } @@ -593,4 +576,101 @@ public class MazeDesigner } } + private static void createMazeLinks(DirectedGraph layout, + ArrayList cores, Random random) + { + // We have 4 objectives here... + // 1. Place the entrance to the maze + // 2. Place internal links connecting the different sections of the maze + // 3. Place links to other dungeons + // 4. Place more internal links to confuse people + + // We need to start by counting the door capacity of each section and + // listing which rooms can have doors or destinations for each section. + int index; + int[] capacity = new int[cores.size()]; + ArrayList[] sourceRooms = (ArrayList[]) Array.newInstance(cores.getClass(), cores.size()); + ArrayList[] destinationRooms = (ArrayList[]) Array.newInstance(cores.getClass(), cores.size()); + + for (index = 0; index < sourceRooms.length; index++) + { + sourceRooms[index] = new ArrayList(); + destinationRooms[index] = new ArrayList(); + capacity[index] = listLinkRooms(cores.get(index).getLayoutNode(), sourceRooms[index], destinationRooms[index]); + } + + // Now we select the room in which to place the entrance. + // We can safely assume all source room lists are non-empty because + // createMazeSections() guarantees that each section has at least + // the capacity for 2 doors. + index = random.nextInt(sourceRooms.length); + createEntranceLink(sourceRooms[index], random.nextInt(sourceRooms[index].size())); + + // The next task is to place internal links. These links must connect + // the different maze sections to create a strongly connected graph. + + } + + private static int listLinkRooms(IGraphNode core, + ArrayList sourceRooms, ArrayList destinationRooms) + { + int capacity = 0; + boolean hasHoles; + RoomData currentRoom; + IGraphNode current; + IGraphNode neighbor; + Stack> ordering = new Stack>(); + HashSet> visited = new HashSet>(); + + visited.add(core); + ordering.add(core); + while (!ordering.isEmpty()) + { + current = ordering.pop(); + hasHoles = false; + + for (IEdge edge : current.outbound()) + { + neighbor = edge.tail(); + if (visited.add(neighbor)) + { + ordering.add(neighbor); + } + } + for (IEdge edge : current.inbound()) + { + neighbor = edge.head(); + if (visited.add(neighbor)) + { + ordering.add(neighbor); + } + if (edge.data().axis() == DoorwayData.Y_AXIS) + { + hasHoles = true; + } + } + + if (!hasHoles) + { + currentRoom = current.data(); + destinationRooms.add(currentRoom); + if (currentRoom.estimateDoorCapacity() > 0) + { + capacity += currentRoom.estimateDoorCapacity(); + sourceRooms.add(currentRoom); + } + } + } + return capacity; + } + + private static void createEntranceLink(ArrayList sources, int index) + { + RoomData entranceRoom = sources.get(index); + LinkPlan.createEntranceLink(entranceRoom); + if (entranceRoom.getRemainingDoorCapacity() == 0) + { + sources.remove(index); + } + } } diff --git a/src/main/java/StevenDimDoors/experimental/MazeLinkData.java b/src/main/java/StevenDimDoors/experimental/MazeLinkData.java deleted file mode 100644 index efff141..0000000 --- a/src/main/java/StevenDimDoors/experimental/MazeLinkData.java +++ /dev/null @@ -1,6 +0,0 @@ -package StevenDimDoors.experimental; - -public class MazeLinkData -{ - -} diff --git a/src/main/java/StevenDimDoors/experimental/RoomData.java b/src/main/java/StevenDimDoors/experimental/RoomData.java index 0334d33..c87c406 100644 --- a/src/main/java/StevenDimDoors/experimental/RoomData.java +++ b/src/main/java/StevenDimDoors/experimental/RoomData.java @@ -4,20 +4,32 @@ import java.util.ArrayList; public class RoomData { + /* Implementation Note: + * Plans for links between rooms are stored in lists rather than a graph, + * even though they duplicate some graph functionality, because there are + * relatively few of them compared to the number of rooms. Moreover, some + * links don't even have destinations because they're intended to lead to + * other dungeons. + */ + + private int capacity; private int distance; private boolean decayed; private PartitionNode partitionNode; - private ArrayList inboundLinks; - private ArrayList outboundLinks; + private ArrayList inboundLinks; + private ArrayList outboundLinks; + private DirectedGraph layout; private IGraphNode layoutNode; public RoomData(PartitionNode partitionNode) { this.partitionNode = partitionNode; - this.inboundLinks = new ArrayList(); - this.outboundLinks = new ArrayList(); + this.inboundLinks = new ArrayList(); + this.outboundLinks = new ArrayList(); this.layoutNode = null; + this.layout = null; this.distance = -1; + this.capacity = -1; this.decayed = false; partitionNode.setData(this); } @@ -34,6 +46,7 @@ public class RoomData public void addToLayout(DirectedGraph layout) { + this.layout = layout; this.layoutNode = layout.addNode(this); } @@ -47,12 +60,12 @@ public class RoomData this.decayed = value; } - public ArrayList getInboundLinks() + public ArrayList getInboundLinks() { return this.inboundLinks; } - public ArrayList getOutboundLinks() + public ArrayList getOutboundLinks() { return this.outboundLinks; } @@ -67,16 +80,54 @@ public class RoomData distance = value; } - public void clear() + public void remove() { + // Remove the room from the partition tree and from the layout graph. + // Also remove any ancestors that become leaf nodes. + PartitionNode parent; + PartitionNode current = partitionNode; + while (current != null && current.isLeaf()) + { + parent = current.parent(); + current.remove(); + current = parent; + } + + // Remove the room from the layout graph + layout.removeNode(layoutNode); + + // Remove any links + while (!inboundLinks.isEmpty()) + inboundLinks.get(inboundLinks.size() - 1).remove(); + + while (!outboundLinks.isEmpty()) + outboundLinks.get(outboundLinks.size() - 1).remove(); + + // Wipe the room's data, as a precaution + layout = null; partitionNode = null; - layoutNode = null; + inboundLinks = null; + outboundLinks = null; } public int estimateDoorCapacity() { + if (capacity >= 0) + return capacity; + int cellsX = (partitionNode.width() - 3) / 2; int cellsZ = (partitionNode.length() - 3) / 2; - return Math.min(cellsX * cellsZ, 3); + capacity = Math.min(cellsX * cellsZ, 3); + return capacity; + } + + public int getRemainingDoorCapacity() + { + return (estimateDoorCapacity() - outboundLinks.size()); + } + + public boolean isProtected() + { + return !inboundLinks.isEmpty() || !outboundLinks.isEmpty(); } } -- 2.39.5 From d192dae945632b777414fc52638c6f17515f295e Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Fri, 11 Apr 2014 20:33:06 -0400 Subject: [PATCH 12/24] Improvements to Saving * Changed saving code to create backups by moving existing files rather than creating copies and deleting the originals. * Removed final call to PocketManager.save() in PocketManager.unload(). Since we no longer check if the caller is the client or server and unload() must be called from both, this prevents clients from trying to save pocket data locally. A final save() call wasn't needed anyway. --- .../mod_pocketDim/core/PocketManager.java | 3 +-- .../mod_pocketDim/saving/DDSaveHandler.java | 26 +++++++------------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java b/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java index 2bf9f59..4eae666 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/core/PocketManager.java @@ -621,8 +621,7 @@ public class PocketManager { throw new IllegalStateException("Pocket dimensions have already been unloaded!"); } - - save(false); + unregisterPockets(); dimensionData = null; rootDimensions = null; diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java index a40e293..a2022c4 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -2,16 +2,11 @@ package StevenDimDoors.mod_pocketDim.saving; import java.io.File; import java.io.FileFilter; -import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; -import java.util.Deque; import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import org.apache.commons.io.FileUtils; import net.minecraftforge.common.DimensionManager; import StevenDimDoors.mod_pocketDim.Point3D; @@ -21,10 +16,7 @@ import StevenDimDoors.mod_pocketDim.core.LinkTypes; import StevenDimDoors.mod_pocketDim.core.NewDimData; import StevenDimDoors.mod_pocketDim.core.PocketManager; import StevenDimDoors.mod_pocketDim.dungeon.DungeonData; -import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonPack; -import StevenDimDoors.mod_pocketDim.dungeon.pack.DungeonType; import StevenDimDoors.mod_pocketDim.helpers.DungeonHelper; -import StevenDimDoors.mod_pocketDim.util.ConfigurationProcessingException; import StevenDimDoors.mod_pocketDim.util.DDLogger; import StevenDimDoors.mod_pocketDim.util.FileFilters; import StevenDimDoors.mod_pocketDim.util.Point4D; @@ -258,8 +250,10 @@ public class DDSaveHandler // Get the save directory path File saveDirectory = new File(mod_pocketDim.instance.getCurrentSavePath() + "/DimensionalDoors/data/"); - File backupDir = new File(saveDirectory+"/backup"); + File backupDirectory = new File(saveDirectory + "/backup"); String savePath = saveDirectory.getAbsolutePath(); + String baseSavePath = savePath + "/dim_"; + String baseBackupPath = backupDirectory.getAbsolutePath() + "/dim_"; if(!saveDirectory.exists()) { @@ -276,11 +270,10 @@ public class DDSaveHandler DimDataProcessor writer = new DimDataProcessor(); for (IPackable dimension : dimensions) { - // Check if the dimension should be saved if (!checkModified || dimension.isModified()) { - if (writeDimension(dimension, writer, savePath + "/dim_",backupDir)) + if (writeDimension(dimension, writer, baseSavePath, baseBackupPath)) { dimension.clearModified(); } @@ -314,17 +307,16 @@ public class DDSaveHandler } } - private static boolean writeDimension(IPackable dimension, DimDataProcessor writer, String basePath, File backupDir) + private static boolean writeDimension(IPackable dimension, DimDataProcessor writer, String basePath, String backupPath) { try { - File saveFile = new File(basePath + (dimension.name() + ".txt")); + File saveFile = new File(basePath + dimension.name() + ".txt"); - //If the savefile already exists, back it up. - if(saveFile.exists()) + // If the save file already exists, back it up. + if (saveFile.exists()) { - FileUtils.copyFileToDirectory(saveFile, backupDir); - saveFile.delete(); + Files.move(saveFile, new File(backupPath + dimension.name() + ".txt")); } writer.writeToFile(saveFile, dimension.pack()); -- 2.39.5 From a2ef6ef90509ab124042d87ef08112c96a3f30e6 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Fri, 11 Apr 2014 20:34:11 -0400 Subject: [PATCH 13/24] Minor Change Changed DDLogger.logger() to private to guarantee that the logger isn't being used anywhere in DD, since that could impact performance on servers. --- src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java b/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java index 1b28510..18ebda0 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/util/DDLogger.java @@ -15,7 +15,8 @@ public class DDLogger this.log.append("Logger started.\n"); } - public static DDLogger logger() + // SenseiKiwi: I changed this to private to guarantee that the logger isn't being used anywhere. + private static DDLogger logger() { if( instance == null) { -- 2.39.5 From 4dca0eb82b23b985f995b3d618682003f4b8bce3 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Fri, 11 Apr 2014 21:32:36 -0400 Subject: [PATCH 14/24] Updated Version Update our version numbers --- build.gradle | 2 +- src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java | 2 +- src/main/resources/mcmod.info | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 487f821..2cbcf59 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ apply plugin: 'forge' -version = "2.2.2RC1-" + System.getenv("BUILD_NUMBER") +version = "2.2.3-" + System.getenv("BUILD_NUMBER") group= "com.stevenrs11.dimdoors" // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "DimensionalDoors" diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java b/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java index 529a032..7b37e1d 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/mod_pocketDim.java @@ -99,7 +99,7 @@ serverPacketHandlerSpec = @SidedPacketHandler(channels = {PacketConstants.CHANNEL_NAME}, packetHandler = ServerPacketHandler.class)) public class mod_pocketDim { - public static final String version = "1.6.4R2.2.2RC1"; + public static final String version = "1.6.4-R2.2.3"; public static final String modid = "dimdoors"; //need to clean up diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index edad775..4687d32 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -6,7 +6,7 @@ "modid": "dimdoors", "name": "Dimensional Doors", "description": "Bend and twist reality itself, creating pocket dimensions, rifts, and much more", -"version": "1.6.4R2.2.2RC1", +"version": "1.6.4-R2.2.3", "credits": "Created by StevenRS11, Coded by StevenRS11 and SenseiKiwi, Logo and Testing by Jaitsu", "logoFile": "/dimdoors_logo.png", "mcversion": "", -- 2.39.5 From 906faf44eb338d05c2f883295a7dcecc8ce5c72e Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Sun, 13 Apr 2014 16:17:42 -0400 Subject: [PATCH 15/24] Tweaked Maze Section Generation Tweaked maze section generation to use a random MAX_DISTANCE for including rooms in a section. Also changed the code to perform room removals as sections are processed rather than deferring them to the end. Deferring removals would cause the algorithm to detect holes from rooms that were going to be removed. This made section generation much stricter than necessary. --- .../experimental/MazeDesigner.java | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java index 0026303..8707d46 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java @@ -351,7 +351,7 @@ public class MazeDesigner // neighboring rooms are added until the necessary space is found or the // search space is exhausted. - final int MAX_DISTANCE = 2; + final int MAX_DISTANCE = 2 + random.nextInt(2); final int MIN_SECTION_ROOMS = 5; final int MIN_SECTION_CAPACITY = 2; @@ -363,18 +363,27 @@ public class MazeDesigner IGraphNode roomNode; ArrayList cores = new ArrayList(); - ArrayList removals = new ArrayList(); ArrayList section = new ArrayList(); + ArrayList> nodes = new ArrayList>(layout.nodeCount()); Queue ordering = new LinkedList(); - // Repeatedly generate sections until all nodes have been visited + // List all graph nodes so that we can iterate over this list instead + // of using the graph's iterator. That avoids the risk of breaking + // the graph's iterator during removals. for (IGraphNode node : layout.nodes()) + { + nodes.add(node); + } + + // Repeatedly generate sections until all nodes have been visited + for (IGraphNode node : nodes) { // If this room hasn't been visited (distance = -1), then use it as the core of a new section - // Otherwise, ignore it, since it was already processed + // Otherwise, ignore it, since it was already processed. Also make sure to check that room + // isn't null, which happens if the room was removed previously. room = node.data(); - if (room.getDistance() < 0) + if (room != null && room.getDistance() < 0) { // Perform a breadth-first search to tag surrounding nodes with distances ordering.add(room); @@ -427,13 +436,10 @@ public class MazeDesigner // The remaining rooms in the ordering are those that are at the // frontier of structure. They must be removed to create a gap - // between this section and other sections. But we can't remove - // the rooms immediately because that could break the iterator - // for the graph. - if (!ordering.isEmpty()) + // between this section and other sections. + while (!ordering.isEmpty()) { - removals.addAll(ordering); - ordering.clear(); + ordering.remove().remove(); } // Check if this section contains enough rooms and capacity for doors @@ -443,16 +449,14 @@ public class MazeDesigner } else { - removals.addAll(section); + // Discard the whole section + for (RoomData target : section) + { + target.remove(); + } } } } - - // Remove all the rooms that were listed for removal - for (RoomData target : removals) - { - target.remove(); - } return cores; } @@ -581,8 +585,8 @@ public class MazeDesigner { // We have 4 objectives here... // 1. Place the entrance to the maze - // 2. Place internal links connecting the different sections of the maze - // 3. Place links to other dungeons + // 2. Place links to other dungeons + // 3. Place internal links connecting the different sections of the maze // 4. Place more internal links to confuse people // We need to start by counting the door capacity of each section and @@ -609,6 +613,7 @@ public class MazeDesigner // The next task is to place internal links. These links must connect // the different maze sections to create a strongly connected graph. + } private static int listLinkRooms(IGraphNode core, -- 2.39.5 From ffe45c729e7c90d699f58ecdef8bc241ee3b091c Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Sun, 13 Apr 2014 16:57:11 -0400 Subject: [PATCH 16/24] Fixed Bugs * Fixed bug with backup directory creation * Fixed two doorways in SK-FractalCage that were supposed to open but had been replaced with Eternal Fabric --- .../mod_pocketDim/saving/DDSaveHandler.java | 9 +++++++-- .../Hub_SK-FractalCage_Open_40.schematic | Bin 10698 -> 10545 bytes 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java index a2022c4..28929dc 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/saving/DDSaveHandler.java @@ -250,17 +250,22 @@ public class DDSaveHandler // Get the save directory path File saveDirectory = new File(mod_pocketDim.instance.getCurrentSavePath() + "/DimensionalDoors/data/"); - File backupDirectory = new File(saveDirectory + "/backup"); String savePath = saveDirectory.getAbsolutePath(); String baseSavePath = savePath + "/dim_"; + File backupDirectory = new File(savePath + "/backup"); String baseBackupPath = backupDirectory.getAbsolutePath() + "/dim_"; - if(!saveDirectory.exists()) + if (!saveDirectory.exists()) { // Create the save directory Files.createParentDirs(saveDirectory); saveDirectory.mkdir(); } + if (!backupDirectory.exists()) + { + // Create the backup directory + backupDirectory.mkdir(); + } // Create and write the blackList writeBlacklist(blacklist, savePath); diff --git a/src/main/resources/schematics/ruins/Hub_SK-FractalCage_Open_40.schematic b/src/main/resources/schematics/ruins/Hub_SK-FractalCage_Open_40.schematic index db1ceaf0a1b71058248e55bb41fe66f71741a77c..d66f82afae8fb11d004c309d96ba871cadb0e5ba 100644 GIT binary patch literal 10545 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 E0M~+x0{{R3 literal 10698 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 Date: Sun, 13 Apr 2014 23:52:51 -0400 Subject: [PATCH 17/24] fixed monoliths everything was terrible --- .../mod_pocketDim/ticking/MobMonolith.java | 253 +++++++----------- .../mod_pocketDimClient/RenderMobObelisk.java | 15 +- 2 files changed, 103 insertions(+), 165 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java b/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java index 36df4dc..d06ffac 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java @@ -27,10 +27,16 @@ import StevenDimDoors.mod_pocketDim.world.PocketProvider; public class MobMonolith extends EntityFlying implements IMob { - public int aggro = 0; + public static final int MAX_AGGRO_RANGE = 35; + public static final int MAX_SOUND_COOLDOWN = 200; + public static final float MAX_AGGRO = 100; + public static final int TEXTURE_STATES = 18; + public float pitchLevel; + + public float aggro = 0; private float soundTime = 0; private byte textureState = 0; - private float scaleFactor = 0; + private int aggroMax; private static DDProperties properties = null; @@ -38,9 +44,8 @@ public class MobMonolith extends EntityFlying implements IMob public MobMonolith(World par1World) { super(par1World); - this.setSize(3F, 9.0F); + this.setSize(3F, 3F); this.noClip=true; - this.scaleFactor = (float) ((rand.nextDouble()/2)+1); this.aggroMax = rand.nextInt(245)+200; if (properties == null) properties = DDProperties.instance(); @@ -55,6 +60,14 @@ public class MobMonolith extends EntityFlying implements IMob @Override public boolean attackEntityFrom(DamageSource par1DamageSource, float par2) { + if (par1DamageSource == DamageSource.inWall) + { + this.posY = posY + 1; + } + else + { + this.aggro = this.aggroMax; + } return false; } @Override @@ -82,17 +95,12 @@ public class MobMonolith extends EntityFlying implements IMob this.getAttributeMap().getAttributeInstance(SharedMonsterAttributes.maxHealth).setAttribute(10); } + @Override public boolean canBePushed() { return false; } - @Override - public float getRenderSizeModifier() - { - return this.scaleFactor; - } - public void setEntityPosition(Entity entity, double x, double y, double z) { entity.lastTickPosX = entity.prevPosX = entity.posX = x; @@ -108,33 +116,6 @@ public class MobMonolith extends EntityFlying implements IMob this.dataWatcher.addObject(16, Byte.valueOf((byte)0)); } - public boolean isClipping() - { - - int i = MathHelper.floor_double(this.boundingBox.minX); - int j = MathHelper.floor_double(this.boundingBox.maxX + 1.0D); - int k = MathHelper.floor_double(this.boundingBox.minY); - int l = MathHelper.floor_double(this.boundingBox.maxY + 1.0D); - int i1 = MathHelper.floor_double(this.boundingBox.minZ); - int j1 = MathHelper.floor_double(this.boundingBox.maxZ + 1.0D); - - for (int k1 = i; k1 < j; ++k1) - { - for (int l1 = k; l1 < l; ++l1) - { - for (int i2 = i1; i2 < j1; ++i2) - { - if(!this.worldObj.isAirBlock(k1, l1, i2)) - { - return true; - } - } - } - } - - - return false; - } @Override public boolean isEntityAlive() { @@ -150,168 +131,131 @@ public class MobMonolith extends EntityFlying implements IMob } super.onEntityUpdate(); - if(this.isClipping()) - { - this.moveEntity(0, .1, 0); - } - EntityPlayer entityPlayer = this.worldObj.getClosestPlayerToEntity(this, 35); + EntityPlayer entityPlayer = this.worldObj.getClosestPlayerToEntity(this,MAX_AGGRO_RANGE); + + //need to always manage aggro level, even if player is out of range. + this.setAggroLevel(entityPlayer); + //these things only matter if the player is in range. if (entityPlayer != null) { - if(this.soundTime<=0) - { - this.playSound(mod_pocketDim.modid+":monk", 1F, 1F); - this.soundTime=100; - } - this.faceEntity(entityPlayer, 1, 1); - - if (shouldAttackPlayer(entityPlayer)) + this.playSounds(entityPlayer); + //teleport the player if the conditions are met + if (aggro >= MAX_AGGRO && !this.worldObj.isRemote && properties.MonolithTeleportationEnabled && !entityPlayer.capabilities.isCreativeMode) { - if (aggro<470) - { - if (rand.nextInt(11)>this.textureState||this.aggro>=300||rand.nextInt(13)>this.textureState&&this.aggroMax>this.aggro) - { - aggro++; - aggro++; - - } - if (this.worldObj.provider instanceof PocketProvider||this.worldObj.getClosestPlayerToEntity(this, 5)!=null) - { - aggro++; - - } - if (aggro>430&&this.soundTime<100) - { - this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",2F, 1F); - this.soundTime=100; - } - if (aggro>445&&this.soundTime<200) - { - this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",5F, 1F); - this.soundTime=200; - } - } - else if (!this.worldObj.isRemote && properties.MonolithTeleportationEnabled && !entityPlayer.capabilities.isCreativeMode) - { - Point4D destination = LimboProvider.getLimboSkySpawn(entityPlayer, properties); - DDTeleporter.teleportEntity(entityPlayer, destination, false); - - this.aggro = 0; - entityPlayer.worldObj.playSoundAtEntity(entityPlayer,mod_pocketDim.modid+":crack",13, 1); - } - if (!(this.worldObj.provider instanceof LimboProvider || this.worldObj.getClosestPlayerToEntity(this, 5) != null) || this.aggro > 300) - { - for (int i = 0; i < -1+this.textureState/2; ++i) - { - entityPlayer.worldObj.spawnParticle("portal", entityPlayer.posX + (this.rand.nextDouble() - 0.5D) * this.width, entityPlayer.posY + this.rand.nextDouble() * entityPlayer.height - 0.75D, entityPlayer.posZ + (this.rand.nextDouble() - 0.5D) * entityPlayer.width, (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), (this.rand.nextDouble() - 0.5D) * 2.0D); - } - } + Point4D destination = LimboProvider.getLimboSkySpawn(entityPlayer, properties); + DDTeleporter.teleportEntity(entityPlayer, destination, false); + this.aggro = 0; + entityPlayer.worldObj.playSoundAtEntity(entityPlayer,mod_pocketDim.modid+":crack",13, 1); } - else if(this.worldObj.provider instanceof PocketProvider) - { - if(aggro 0) + { + this.aggro = this.aggro -(this.aggro/25); + } + if(player != null) + { + //monoliths increase aggro slightly if the player is near, but slowly and to a cap. + float distance = this.getDistanceToEntity(player); + aggro+= 1.5-(distance/this.MAX_AGGRO_RANGE); + + //rapidly increase aggro if the monolith has line of sight to the player. + if(player.canEntityBeSeen(this)) + { + //prevent monoliths from teleporting the player in limbo + if(this.worldObj.provider instanceof LimboProvider) { - if(rand.nextInt(3)==0) - { - aggro++; - } + aggro+=1.5; } else { - if(aggro>0) - { - aggro--; - } + this.spawnParticles(player); + aggro+=3; } } } - else + + //convert the aggro counter to one of the texture states, and set it. + this.textureState = (byte) ((this.TEXTURE_STATES/this.MAX_AGGRO)*this.aggro); + if(this.textureState>TEXTURE_STATES) { - if(aggro>0) - { - aggro--; - - if(rand.nextBoolean()) - { - aggro--; - } - } + textureState = TEXTURE_STATES; } - if (soundTime>=0) - { - soundTime--; - } - this.textureState= (byte) (this.aggro/25); if (!this.worldObj.isRemote) { this.dataWatcher.updateObject(16, Byte.valueOf(this.textureState)); } } - - - private boolean shouldAttackPlayer(EntityPlayer par1EntityPlayer) + + /** + * Plays sounds at different levels of aggro, using soundTime to prevent too many sounds at once. + * @param entityPlayer + */ + private void playSounds(EntityPlayer entityPlayer) { - return par1EntityPlayer.canEntityBeSeen(this); + float aggroPercent = (aggro/MAX_AGGRO); + if(this.soundTime<=0) + { + this.playSound(mod_pocketDim.modid+":monk", 1F, 1F); + this.soundTime=100; + } + if ((aggroPercent>.80)&&this.soundTime<100) + { + this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",2, 1F); + this.soundTime=200; + } + if ((aggroPercent>.95)&&this.soundTime<200) + { + this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",5, 1F); + this.soundTime=250; + } + this.soundTime--; } - - public boolean attackEntityFrom(DamageSource par1DamageSource, int par2) + + private void spawnParticles(EntityPlayer player) { - if (par1DamageSource == DamageSource.inWall) + for (int i = 1; i < (10*(aggro/MAX_AGGRO)); ++i) { - this.posY = posY + 1; + player.worldObj.spawnParticle("portal", player.posX + (this.rand.nextDouble() - 0.5D) * this.width, + player.posY + this.rand.nextDouble() * player.height - 0.75D, + player.posZ + (this.rand.nextDouble() - 0.5D) * player.width, + (this.rand.nextDouble() - 0.5D) * 2.0D, -this.rand.nextDouble(), + (this.rand.nextDouble() - 0.5D) * 2.0D); } - else - { - this.aggro = this.aggroMax; - } - return false; } - + @Override public void faceEntity(Entity par1Entity, float par2, float par3) { double d0 = par1Entity.posX - this.posX; double d1 = par1Entity.posZ - this.posZ; - double d2; - - if (par1Entity instanceof EntityLiving) - { - EntityLiving entityliving = (EntityLiving)par1Entity; - d2 = entityliving.posY + entityliving.getEyeHeight() - (this.posY + this.getEyeHeight()); - } - else - { - d2 = (par1Entity.boundingBox.minY + par1Entity.boundingBox.maxY) - (this.posY + this.getEyeHeight()); - } - + double d2 = (par1Entity.posY + par1Entity.getEyeHeight()) - (this.posY +this.getEyeHeight()); double d3 = MathHelper.sqrt_double(d0 * d0 + d1 * d1); float f2 = (float)(Math.atan2(d1, d0) * 180.0D / Math.PI) - 90.0F; - float f3 = (float)(-(Math.atan2(d2, d3) * 180.0D / Math.PI)); - this.rotationPitch = f3; - this.rotationYaw = f2; - + this.pitchLevel = (float)-((Math.atan(d2/d3) )* 180.0D / Math.PI); + this.rotationYaw = f2; this.rotationYawHead=f2; this.renderYawOffset=this.rotationYaw; } - @Override - public float getRotationYawHead() - { - return 0.0F; - } + @Override public void writeEntityToNBT(NBTTagCompound par1NBTTagCompound) { super.writeEntityToNBT(par1NBTTagCompound); par1NBTTagCompound.setFloat("soundTime", this.soundTime); - par1NBTTagCompound.setInteger("aggro", this.aggro); + par1NBTTagCompound.setFloat("aggro", this.aggro); par1NBTTagCompound.setInteger("aggroMax", this.aggroMax); par1NBTTagCompound.setByte("textureState", this.textureState); - par1NBTTagCompound.setFloat("scaleFactor", this.scaleFactor); } @Override @@ -319,10 +263,11 @@ public class MobMonolith extends EntityFlying implements IMob { super.readEntityFromNBT(par1NBTTagCompound); this.soundTime = par1NBTTagCompound.getFloat("soundTime"); - this.aggro = par1NBTTagCompound.getInteger("aggro"); + + //make them load with half aggro so they dont instantly teleport players + this.aggro = par1NBTTagCompound.getFloat("aggro")/2; this.aggroMax = par1NBTTagCompound.getInteger("aggroMax"); this.textureState = par1NBTTagCompound.getByte("textureState"); - this.scaleFactor = par1NBTTagCompound.getFloat("scaleFactor"); } @Override diff --git a/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java b/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java index b21e084..48dbfa9 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java +++ b/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java @@ -63,25 +63,18 @@ public class RenderMobObelisk extends RenderLiving GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDepthMask(false); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - this.mainModel.onGround = this.renderSwingProgress(par1EntityLivingBase, par9); try { float interpolatedYaw = this.interpolateRotation(par1EntityLivingBase.prevRenderYawOffset, par1EntityLivingBase.renderYawOffset, par9); - float interpolatedYawHead = this.interpolateRotation(par1EntityLivingBase.prevRotationYawHead, par1EntityLivingBase.rotationYawHead, par9); - float rotation; - float pitch = par1EntityLivingBase.prevRotationPitch + (par1EntityLivingBase.rotationPitch - par1EntityLivingBase.prevRotationPitch) * par9; this.renderLivingAt(par1EntityLivingBase, x, y, z); - - rotation = this.handleRotationFloat(par1EntityLivingBase, par9); - this.rotateCorpse(par1EntityLivingBase, rotation, interpolatedYaw, par9); - float f6 = 0.0625F; + this.rotateCorpse(par1EntityLivingBase, 0, 0, par9); GL11.glEnable(GL12.GL_RESCALE_NORMAL); GL11.glScalef(-1.0F, -1.0F, 1.0F); this.preRenderCallback(par1EntityLivingBase, par9); - GL11.glTranslatef(0.0F, -24.0F * f6 - 0.0078125F, 0.0F); - - this.renderModel(par1EntityLivingBase, 0, 0, rotation, interpolatedYawHead - interpolatedYaw, pitch, f6); + GL11.glRotatef(interpolatedYaw , 0.0F, 1.0F, 0.0F); + GL11.glRotatef(((MobMonolith)par1EntityLivingBase).pitchLevel , 1.0F, 0.0F, 0.0F); + this.renderModel(par1EntityLivingBase, 0, 0, 0,0, 0, 0); OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); -- 2.39.5 From 81b48158bd19b8b7441f3cf92e0cf59c44a72ae6 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Mon, 14 Apr 2014 22:24:59 -0400 Subject: [PATCH 18/24] Progress on Maze Generation * Finished implementing link planning for mazes. Doors aren't placed yet because that's up to Decorators and those haven't been implemented yet. * Added bounding walls to mazes. * Added decay effects to mazes. --- .../StevenDimDoors/experimental/LinkPlan.java | 85 +++++-- .../experimental/MazeBuilder.java | 139 +++++++++-- .../experimental/MazeDesign.java | 6 +- .../experimental/MazeDesigner.java | 235 ++++++++++++------ .../experimental/SectionData.java | 192 ++++++++++++++ .../StevenDimDoors/mod_pocketDim/Point3D.java | 7 + .../mod_pocketDim/world/PocketBuilder.java | 2 +- 7 files changed, 544 insertions(+), 122 deletions(-) create mode 100644 src/main/java/StevenDimDoors/experimental/SectionData.java diff --git a/src/main/java/StevenDimDoors/experimental/LinkPlan.java b/src/main/java/StevenDimDoors/experimental/LinkPlan.java index 3fe6f75..fbd90c1 100644 --- a/src/main/java/StevenDimDoors/experimental/LinkPlan.java +++ b/src/main/java/StevenDimDoors/experimental/LinkPlan.java @@ -1,52 +1,47 @@ package StevenDimDoors.experimental; +import StevenDimDoors.mod_pocketDim.Point3D; + public class LinkPlan { private RoomData source; private RoomData destination; - private boolean entrance; + private Point3D sourcePoint; + private Point3D destinationPoint; + private final boolean entrance; + private final boolean internal; - private LinkPlan(RoomData source, RoomData destination, boolean entrance) - { - this.source = source; - this.destination = destination; - this.entrance = entrance; - } - - public static LinkPlan createInternalLink(RoomData source, RoomData destination) + private LinkPlan(RoomData source, boolean entrance, boolean internal) { if (source == null) { throw new IllegalArgumentException("source cannot be null."); } - if (destination == null) - { - throw new IllegalArgumentException("destination cannot be null."); - } - LinkPlan plan = new LinkPlan(source, destination, false); + this.source = source; + this.destination = null; + this.sourcePoint = null; + this.destinationPoint = null; + this.entrance = entrance; + this.internal = internal; + } + + public static LinkPlan createInternalLink(RoomData source) + { + LinkPlan plan = new LinkPlan(source, false, true); source.getOutboundLinks().add(plan); - destination.getInboundLinks().add(plan); return plan; } public static LinkPlan createEntranceLink(RoomData source) { - if (source == null) - { - throw new IllegalArgumentException("source cannot be null."); - } - LinkPlan plan = new LinkPlan(source, null, true); + LinkPlan plan = new LinkPlan(source, true, false); source.getOutboundLinks().add(plan); return plan; } public static LinkPlan createDungeonLink(RoomData source) { - if (source == null) - { - throw new IllegalArgumentException("source cannot be null."); - } - LinkPlan plan = new LinkPlan(source, null, false); + LinkPlan plan = new LinkPlan(source, false, false); source.getOutboundLinks().add(plan); return plan; } @@ -68,7 +63,7 @@ public class LinkPlan public boolean isInternal() { - return (destination != null); + return internal; } public void remove() @@ -84,4 +79,42 @@ public class LinkPlan destination = null; } } + + public void setDestination(RoomData destination) + { + if (!internal) + { + throw new IllegalStateException("LinkPlan.setDestination() is only applicable to internal links."); + } + if (this.destination != null) + { + throw new IllegalStateException("destination can only be set once."); + } + if (destination == null) + { + throw new IllegalArgumentException("destination cannot be null."); + } + this.destination = destination; + destination.getInboundLinks().add(this); + } + + public Point3D sourcePoint() + { + return this.sourcePoint; + } + + public Point3D destinationPoint() + { + return this.destinationPoint; + } + + public void setSourcePoint(Point3D value) + { + this.sourcePoint = value; + } + + public void setDestinationPoint(Point3D value) + { + this.destinationPoint = value; + } } diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index 6505f99..760c399 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -1,18 +1,23 @@ package StevenDimDoors.experimental; +import java.util.ArrayList; import java.util.Random; +import java.util.Stack; import net.minecraft.block.Block; import net.minecraft.world.World; import net.minecraft.world.chunk.Chunk; import net.minecraft.world.chunk.storage.ExtendedBlockStorage; import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.config.DDProperties; public class MazeBuilder { + private static final int POCKET_WALL_GAP = 4; + private MazeBuilder() { } - public static void generate(World world, int x, int y, int z, Random random) + public static void generate(World world, int x, int y, int z, Random random, DDProperties properties) { // ISSUE FOR LATER: The room needs to be shifted so as to be centered on its entrance @@ -22,16 +27,85 @@ public class MazeBuilder buildRooms(design.getLayout(), world, offset); carveDoorways(design.getLayout(), world, offset, decay, random); - - placeDoors(design.getLayout(), world, offset); - applyRandomDestruction(design, world, offset, decay, random); + decorateRooms(design.getLayout(), world, offset); + buildPocketWalls(design, world, offset, properties); } private static void applyRandomDestruction(MazeDesign design, World world, Point3D offset, SphereDecayOperation decay, Random random) { - //final int DECAY_BOX_SIZE = 8 + final int DECAY_BOX_SIZE = 7; + final int DECAY_OPERATIONS = 5 + random.nextInt(5); + final int DECAY_ATTEMPTS = 20; + + int x, y, z; + int successes = 0; + int attempts = 0; + PartitionNode root = design.getRootPartition(); + + for (; successes < DECAY_OPERATIONS && attempts < DECAY_ATTEMPTS; attempts++) + { + // Select the coordinates at which to apply the decay operation + x = random.nextInt(design.width()) - DECAY_BOX_SIZE / 2; + y = random.nextInt(design.height()) - DECAY_BOX_SIZE / 2; + z = random.nextInt(design.length()) - DECAY_BOX_SIZE / 2; + + // Check that the decay operation would not impact any protected areas + // and mark the affected areas as decayed + if (markDecayArea(x, y, z, DECAY_BOX_SIZE, root)) + { + // Apply decay + decay.apply(world, offset.getX() + x, offset.getY() + y, offset.getZ() + z, + DECAY_BOX_SIZE, DECAY_BOX_SIZE, DECAY_BOX_SIZE); + successes++; + } + } + } + + private static boolean markDecayArea(int x, int y, int z, int DECAY_BOX_SIZE, PartitionNode root) + { + // Check if a given PartitionNode intersects the decay area. If it's a leaf, then check + // if it's protected or not. Otherwise, check its children. The specific area is valid + // if and only if there are no protected rooms and at least one (unprotected) room in it. + // Also list the unprotected rooms to mark them if the decay operation will proceed. + + RoomData room; + PartitionNode partition; + ArrayList targets = new ArrayList(); + Stack> nodes = new Stack>(); + BoundingBox decayBounds = new BoundingBox(x, y, z, DECAY_BOX_SIZE, DECAY_BOX_SIZE, DECAY_BOX_SIZE); + + // Use depth-first search to explore all intersecting partitions + nodes.push(root); + while (!nodes.isEmpty()) + { + partition = nodes.pop(); + if (decayBounds.intersects(partition)) + { + if (partition.isLeaf()) + { + room = partition.getData(); + if (room.isProtected()) + return false; + targets.add(room); + } + else + { + if (partition.leftChild() != null) + nodes.push(partition.leftChild()); + if (partition.rightChild() != null) + nodes.push(partition.rightChild()); + } + } + } + // If execution has reached this point, then there were no protected rooms. + // Mark all intersecting rooms as decayed. + for (RoomData target : targets) + { + target.setDecayed(true); + } + return !targets.isEmpty(); } private static void buildRooms(DirectedGraph layout, World world, Point3D offset) @@ -43,19 +117,25 @@ public class MazeBuilder } } - private static void placeDoors(DirectedGraph layout, World world, Point3D offset) + private static void decorateRooms(DirectedGraph layout, World world, Point3D offset) { + RoomData room; + PartitionNode partition; + ArrayList links = new ArrayList(); + + // Iterate over all rooms and apply decorators for (IGraphNode node : layout.nodes()) { - RoomData room = node.data(); - Point3D minCorner = room.getPartitionNode().minCorner(); - if (!room.getOutboundLinks().isEmpty()) - { - setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 1, - offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0); - setBlockDirectly(world, offset.getX() + minCorner.getX(), offset.getY() + minCorner.getY() + 2, - offset.getZ() + minCorner.getZ(), Block.glowStone.blockID, 0); - } + room = node.data(); + partition = room.getPartitionNode(); + links.addAll(room.getOutboundLinks()); + + // TODO: Add decorator code here! + } + // Iterate over all links plans and place links in the world + for (LinkPlan link : links) + { + // TODO: Add link placement code here! } } @@ -70,12 +150,19 @@ public class MazeBuilder { for (IEdge passage : node.outbound()) { + // Carve out the passage doorway = passage.data(); axis = doorway.axis(); lower = doorway.minCorner(); carveDoorway(world, axis, offset.getX() + lower.getX(), offset.getY() + lower.getY(), offset.getZ() + lower.getZ(), doorway.width(), doorway.height(), doorway.length(), decay, random); + + // If this is a vertical passage, then mark the upper room as decayed + if (axis == DoorwayData.Y_AXIS) + { + passage.tail().data().setDecayed(true); + } } } } @@ -86,6 +173,8 @@ public class MazeBuilder final int MIN_DOUBLE_DOOR_SPAN = 10; int gap; + int rx; + int rz; switch (axis) { case DoorwayData.X_AXIS: @@ -150,9 +239,10 @@ public class MazeBuilder { gap = 6; } - decay.apply(world, - x + random.nextInt(width - gap - 1) + 1, y - 1, - z + random.nextInt(length - gap - 1) + 1, gap, 4, gap); + rx = x + random.nextInt(width - gap - 1) + 1; + rz = z + random.nextInt(length - gap - 1) + 1; + carveHole(world, rx + gap / 2, y, rz + gap / 2); + decay.apply(world, rx, y - 1, rz, gap, 4, gap); } else { @@ -184,6 +274,19 @@ public class MazeBuilder setBlockDirectly(world, x, y + 1, z, 0, 0); } + private static void buildPocketWalls(MazeDesign design, World world, Point3D offset, DDProperties properties) + { + // Build the inner Fabric of Reality box + Point3D minCorner = new Point3D(-POCKET_WALL_GAP - 1, -POCKET_WALL_GAP - 1, -POCKET_WALL_GAP - 1); + Point3D maxCorner = new Point3D(design.width() + POCKET_WALL_GAP, design.height() + POCKET_WALL_GAP, design.length() + POCKET_WALL_GAP); + buildBox(world, offset, minCorner, maxCorner, properties.FabricBlockID, 0); + + // Build the outer Eternal Fabric box + minCorner.add(-1, -1, -1); + maxCorner.add(1, 1, 1); + buildBox(world, offset, minCorner, maxCorner, properties.PermaFabricBlockID, 0); + } + private static void buildBox(World world, Point3D offset, Point3D minCorner, Point3D maxCorner, int blockID, int metadata) { int minX = minCorner.getX() + offset.getX(); diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesign.java b/src/main/java/StevenDimDoors/experimental/MazeDesign.java index c88f1cd..16c190b 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesign.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesign.java @@ -4,16 +4,16 @@ import java.util.ArrayList; public class MazeDesign { - private PartitionNode root; + private PartitionNode root; private DirectedGraph layout; - public MazeDesign(PartitionNode root, DirectedGraph layout) + public MazeDesign(PartitionNode root, DirectedGraph layout) { this.root = root; this.layout = layout; } - public PartitionNode getRootPartition() + public PartitionNode getRootPartition() { return root; } diff --git a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java index 8707d46..53113cb 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeDesigner.java +++ b/src/main/java/StevenDimDoors/experimental/MazeDesigner.java @@ -589,93 +589,180 @@ public class MazeDesigner // 3. Place internal links connecting the different sections of the maze // 4. Place more internal links to confuse people - // We need to start by counting the door capacity of each section and - // listing which rooms can have doors or destinations for each section. + // We need to start by building up data for each section, such as their + // door capacities and the rooms available for placing doors. int index; - int[] capacity = new int[cores.size()]; - ArrayList[] sourceRooms = (ArrayList[]) Array.newInstance(cores.getClass(), cores.size()); - ArrayList[] destinationRooms = (ArrayList[]) Array.newInstance(cores.getClass(), cores.size()); + int count; + SectionData selection; + SectionData destination; + ArrayList allSections; + ArrayList usableSections; - for (index = 0; index < sourceRooms.length; index++) - { - sourceRooms[index] = new ArrayList(); - destinationRooms[index] = new ArrayList(); - capacity[index] = listLinkRooms(cores.get(index).getLayoutNode(), sourceRooms[index], destinationRooms[index]); - } - - // Now we select the room in which to place the entrance. - // We can safely assume all source room lists are non-empty because - // createMazeSections() guarantees that each section has at least - // the capacity for 2 doors. - index = random.nextInt(sourceRooms.length); - createEntranceLink(sourceRooms[index], random.nextInt(sourceRooms[index].size())); - - // The next task is to place internal links. These links must connect - // the different maze sections to create a strongly connected graph. - - - } - - private static int listLinkRooms(IGraphNode core, - ArrayList sourceRooms, ArrayList destinationRooms) - { - int capacity = 0; - boolean hasHoles; - RoomData currentRoom; - IGraphNode current; - IGraphNode neighbor; - Stack> ordering = new Stack>(); - HashSet> visited = new HashSet>(); - - visited.add(core); - ordering.add(core); - while (!ordering.isEmpty()) - { - current = ordering.pop(); - hasHoles = false; - - for (IEdge edge : current.outbound()) + // Check if there is only one section. Our concerns differ depending + // on whether there is one or more than one. + if (cores.size() > 1) + { + // More than 1 section + allSections = new ArrayList(cores.size()); + for (RoomData core : cores) { - neighbor = edge.tail(); - if (visited.add(neighbor)) - { - ordering.add(neighbor); - } + allSections.add( SectionData.createFromCore(core.getLayoutNode()) ); } - for (IEdge edge : current.inbound()) + usableSections = (ArrayList) allSections.clone(); + + // Select the room in which to place the entrance. + // We can safely consider all sections because createMazeSections() + // guarantees that each one has at least the capacity for 2 doors. + // Remove the selected section if it falls below a capacity of 2 + // since we need to leave at least 1 capacity for section linking. + index = random.nextInt(usableSections.size()); + selection = usableSections.get(index); + selection.createEntranceLink(random); + if (selection.capacity() <= 1) { - neighbor = edge.head(); - if (visited.add(neighbor)) + usableSections.remove(index); + } + + // Place 3 to 4 dungeon doors in random sections + // Remove any sections that fall under a capacity of 2. + count = 3 + random.nextInt(2); + for (; count > 0 && !usableSections.isEmpty(); count--) + { + index = random.nextInt(usableSections.size()); + selection = usableSections.get(index); + selection.createDungeonLink(random); + if (selection.capacity() <= 1) { - ordering.add(neighbor); - } - if (edge.data().axis() == DoorwayData.Y_AXIS) - { - hasHoles = true; + usableSections.remove(index); } } - if (!hasHoles) + // The next task is to place internal links. These links must connect + // the different maze sections to create a strongly connected graph. + linkMazeSections(allSections, random); + + // Add 1 to 3 extra internal links to confuse people + usableSections.clear(); + for (SectionData section : allSections) { - currentRoom = current.data(); - destinationRooms.add(currentRoom); - if (currentRoom.estimateDoorCapacity() > 0) + if (section.capacity() > 0) { - capacity += currentRoom.estimateDoorCapacity(); - sourceRooms.add(currentRoom); + usableSections.add(section); + } + } + count = 1 + random.nextInt(3); + for (; count > 0 && !usableSections.isEmpty(); count--) + { + index = random.nextInt(usableSections.size()); + selection = usableSections.get(index); + destination = allSections.get( random.nextInt(allSections.size()) ); + selection.reserveSectionLink(destination, random); + if (selection.capacity() == 0) + { + usableSections.remove(index); + } + } + + // Finally, make sure to process all reservations for section links. + for (SectionData section : allSections) + { + section.processReservedLinks(random); + } + } + else + { + // Only 1 section + selection = SectionData.createFromCore(cores.get(0).getLayoutNode()); + // Place entrance door in a random room + selection.createEntranceLink(random); + // Place 3 to 4 dungeon doors or fewer, based on capacity + count = Math.min(3 + random.nextInt(2), selection.capacity()); + for (; count > 0; count--) + { + selection.createDungeonLink(random); + } + } + } + + private static void linkMazeSections(ArrayList sections, Random random) + { + // This algorithm constructs links sections together using Dimensional Doors + // to create a random strongly connected graph. It takes into account capacity + // constraints as well. We assume that all sections have at least 1 door capacity. + final int EXTENSION_CHANCE = 2; + final int MAX_EXTENSION_CHANCE = 3; + + int index; + SectionData start; + SectionData current; + SectionData next; + + // Total spare capacity of the sections not in "remaining" + int capacity; + // Sections not in the graph + ArrayList remaining = (ArrayList) sections.clone(); + // Sections that are part of an incomplete cycle + ArrayList attached = new ArrayList(sections.size()); + // Sections that are part of a cycle and thus strongly connected + ArrayList connected = new ArrayList(sections.size()); + // Sections that are part of a cycle and have spare capacity - used to start new cycles + ArrayList starters = new ArrayList(sections.size()); + + // Shuffle remaining to achieve randomness + Collections.shuffle(remaining, random); + // Remove the starting node to serve as the base of our strongly connected graph + start = remaining.remove(remaining.size() - 1); + starters.add(start); + connected.add(start); + capacity = start.capacity(); + + // Repeat until all sections are connected + while (!remaining.isEmpty()) + { + // Select a section from which to start a new cycle + index = random.nextInt(starters.size()); + start = starters.get(index); + // Select the first new section in the cycle and link to it + current = remaining.remove(remaining.size() - 1); + attached.add(current); + start.reserveSectionLink(current, random); + // Add the current section's capacity to the total, but subtract two to account + // for the link just created and for the future link from this section to another + capacity += current.capacity() - 2; + // Remove the starting section from starters if it has exhausted its capacity + if (start.capacity() == 0) + { + starters.remove(index); + } + + // Continue attaching sections to the partial cycle while there are are still sections + // left to be added and we have no spare capacity. Or we could randomly decide to + // continue even with spare capacity. Spare capacity means we could start a new cycle + // safely and still achieve strong connectivity. Randomness here influences the kinds + // of graphs we can get. + while (!remaining.isEmpty() && (capacity == 0 || + random.nextInt(MAX_EXTENSION_CHANCE) < EXTENSION_CHANCE)) + { + next = remaining.remove(remaining.size() - 1); + attached.add(next); + current.reserveSectionLink(next, random); + // Account for this section's capacity, but subtract one for + // the future link that will connect this section to another + capacity += next.capacity() - 1; + current = next; + } + next = connected.get(random.nextInt(connected.size())); + current.reserveSectionLink(next, random); + for (SectionData section : attached) + { + connected.add(section); + if (section.capacity() > 0) + { + starters.add(section); } } } - return capacity; - } - - private static void createEntranceLink(ArrayList sources, int index) - { - RoomData entranceRoom = sources.get(index); - LinkPlan.createEntranceLink(entranceRoom); - if (entranceRoom.getRemainingDoorCapacity() == 0) - { - sources.remove(index); - } + + // Done! At this point, all sections are connected. } } diff --git a/src/main/java/StevenDimDoors/experimental/SectionData.java b/src/main/java/StevenDimDoors/experimental/SectionData.java new file mode 100644 index 0000000..22d240a --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/SectionData.java @@ -0,0 +1,192 @@ +package StevenDimDoors.experimental; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Random; +import java.util.Stack; + +public class SectionData +{ + // Specifies the chance of selecting a destination from protectedRooms + // rather than from destinationRooms (which will then become protected) + private static final int PROTECTED_DESTINATION_CHANCE = 4; + private static final int MAX_PROTECTED_DESTINATION_CHANCE = 5; + + private int capacity; + private ArrayList sourceRooms; + private ArrayList protectedRooms; + private ArrayList destinationRooms; + private ArrayList reservations; + + private SectionData(ArrayList sourceRooms, ArrayList destinationRooms, int capacity) + { + this.capacity = capacity; + this.sourceRooms = sourceRooms; + this.destinationRooms = destinationRooms; + this.protectedRooms = new ArrayList(); + this.reservations = new ArrayList(); + } + + public static SectionData createFromCore(IGraphNode core) + { + int capacity = 0; + ArrayList sourceRooms = new ArrayList(); + ArrayList destinationRooms = new ArrayList(); + + boolean hasHoles; + RoomData currentRoom; + IGraphNode current; + IGraphNode neighbor; + Stack> ordering = new Stack>(); + HashSet> visited = new HashSet>(); + + visited.add(core); + ordering.add(core); + while (!ordering.isEmpty()) + { + current = ordering.pop(); + hasHoles = false; + + for (IEdge edge : current.outbound()) + { + neighbor = edge.tail(); + if (visited.add(neighbor)) + { + ordering.add(neighbor); + } + } + for (IEdge edge : current.inbound()) + { + neighbor = edge.head(); + if (visited.add(neighbor)) + { + ordering.add(neighbor); + } + if (edge.data().axis() == DoorwayData.Y_AXIS) + { + hasHoles = true; + } + } + + if (!hasHoles) + { + currentRoom = current.data(); + destinationRooms.add(currentRoom); + if (currentRoom.estimateDoorCapacity() > 0) + { + capacity += currentRoom.estimateDoorCapacity(); + sourceRooms.add(currentRoom); + } + } + } + return new SectionData(sourceRooms, destinationRooms, capacity); + } + + public int capacity() + { + return capacity; + } + + public void createEntranceLink(Random random) + { + int index = random.nextInt(sourceRooms.size()); + RoomData room = sourceRooms.get(index); + LinkPlan.createEntranceLink(room); + if (room.getRemainingDoorCapacity() == 0) + { + sourceRooms.remove(index); + } + // It's okay to check containment in this list because + // the number of protected rooms is expected to be small + if (!protectedRooms.contains(room)) + { + protectedRooms.add(room); + } + capacity--; + } + + public void createDungeonLink(Random random) + { + int index = random.nextInt(sourceRooms.size()); + RoomData room = sourceRooms.get(index); + LinkPlan.createDungeonLink(room); + if (room.getRemainingDoorCapacity() == 0) + { + sourceRooms.remove(index); + } + // It's okay to check containment in this list because + // the number of protected rooms is expected to be small + if (!protectedRooms.contains(room)) + { + protectedRooms.add(room); + } + capacity--; + } + + public void reserveSectionLink(SectionData destination, Random random) + { + // This method "reserves" a link by decrementing the capacity of this + // section and assigning a source room to the link. However, assigning + // its destination in a particular section is deferred. Why? + + // We favor using source rooms as destinations to cut down the number + // of rooms that have to be marked as protected against decay effects. + // We defer assigning a destination until after all source rooms are + // known so that we have that information available. Otherwise, + // destination selection would be biased toward non-source rooms and + // rooms with dungeon doors, which are placed before section links. + + int index = random.nextInt(sourceRooms.size()); + RoomData room = sourceRooms.get(index); + destination.reserveDestination(LinkPlan.createInternalLink(room)); + if (room.getRemainingDoorCapacity() == 0) + { + sourceRooms.remove(index); + } + // It's okay to check containment in this list because + // the number of protected rooms is expected to be small + if (!protectedRooms.contains(room)) + { + protectedRooms.add(room); + } + capacity--; + } + + public void processReservedLinks(Random random) + { + for (LinkPlan link : reservations) + { + link.setDestination( getLinkDestination(random) ); + } + reservations.clear(); + } + + private void reserveDestination(LinkPlan link) + { + reservations.add(link); + } + + private RoomData getLinkDestination(Random random) + { + RoomData destination; + + // Choose whether to select a room that is already protected or select + // from all possible destination rooms. Note that some destination rooms + // may also be protected rooms already. + if (random.nextInt(MAX_PROTECTED_DESTINATION_CHANCE) < PROTECTED_DESTINATION_CHANCE) + { + destination = protectedRooms.get( random.nextInt(protectedRooms.size()) ); + } + else + { + destination = destinationRooms.get( random.nextInt(destinationRooms.size()) ); + // It's okay to check containment in this list because + // the number of protected rooms is expected to be small + if (!protectedRooms.contains(destination)) + { + protectedRooms.add(destination); + } + } + return destination; + } +} diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java b/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java index f639ce1..627bdab 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java @@ -55,6 +55,13 @@ public class Point3D implements Serializable { { return this.z = z; } + + public void add(int x, int y, int z) + { + this.x += x; + this.y += y; + this.z += z; + } @Override public Point3D clone() diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java b/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java index 913c282..462033a 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/world/PocketBuilder.java @@ -477,7 +477,7 @@ public class PocketBuilder } */ - MazeBuilder.generate(world, x, y, z, random); + MazeBuilder.generate(world, x, y, z, random, properties); //Build the door int doorOrientation = BlockRotator.transformMetadata(BlockRotator.EAST_DOOR_METADATA, orientation - BlockRotator.EAST_DOOR_METADATA + 2, properties.DimensionalDoorID); -- 2.39.5 From bce329c8fb539881a0a885d5fa028aabe91ca629 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Tue, 15 Apr 2014 01:35:14 -0400 Subject: [PATCH 19/24] Added Maze Decorators Added Decorators for use in mazes. I've added several decorators but all of them are stubs for now. --- .../experimental/MazeBuilder.java | 22 ++++++-- .../decorators/BaseDecorator.java | 15 ++++++ .../decorators/DecoratorFinder.java | 51 +++++++++++++++++++ .../decorators/DefaultDoorDecorator.java | 23 +++++++++ .../decorators/LinkDestinationDecorator.java | 23 +++++++++ .../decorators/TorchDecorator.java | 23 +++++++++ 6 files changed, 153 insertions(+), 4 deletions(-) create mode 100644 src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java create mode 100644 src/main/java/StevenDimDoors/experimental/decorators/DecoratorFinder.java create mode 100644 src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java create mode 100644 src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java create mode 100644 src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index 760c399..592b2d0 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -8,12 +8,16 @@ 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.experimental.decorators.BaseDecorator; +import StevenDimDoors.experimental.decorators.DecoratorFinder; import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.config.DDProperties; public class MazeBuilder { private static final int POCKET_WALL_GAP = 4; + private static final int DECORATION_CHANCE = 1; + private static final int MAX_DECORATION_CHANCE = 4; private MazeBuilder() { } @@ -28,7 +32,7 @@ public class MazeBuilder buildRooms(design.getLayout(), world, offset); carveDoorways(design.getLayout(), world, offset, decay, random); applyRandomDestruction(design, world, offset, decay, random); - decorateRooms(design.getLayout(), world, offset); + decorateRooms(design.getLayout(), world, offset, random, properties); buildPocketWalls(design, world, offset, properties); } @@ -117,9 +121,11 @@ public class MazeBuilder } } - private static void decorateRooms(DirectedGraph layout, World world, Point3D offset) + private static void decorateRooms(DirectedGraph layout, + World world, Point3D offset, Random random, DDProperties properties) { RoomData room; + BaseDecorator decorator; PartitionNode partition; ArrayList links = new ArrayList(); @@ -129,8 +135,16 @@ public class MazeBuilder room = node.data(); partition = room.getPartitionNode(); links.addAll(room.getOutboundLinks()); - - // TODO: Add decorator code here! + // Protected rooms must be decorated because they have links. + // Otherwise, choose randomly whether to decorate. + if (room.isProtected() && random.nextInt(MAX_DECORATION_CHANCE) < DECORATION_CHANCE) + { + decorator = DecoratorFinder.find(room, random); + if (decorator != null) + { + decorator.decorate(room, random, properties); + } + } } // Iterate over all links plans and place links in the world for (LinkPlan link : links) diff --git a/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java new file mode 100644 index 0000000..05bd70f --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java @@ -0,0 +1,15 @@ +package StevenDimDoors.experimental.decorators; + +import java.util.Random; + +import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.config.DDProperties; + +public abstract class BaseDecorator +{ + public BaseDecorator() { } + + public abstract boolean canDecorate(RoomData room); + + public abstract boolean decorate(RoomData room, Random random, DDProperties properties); +} diff --git a/src/main/java/StevenDimDoors/experimental/decorators/DecoratorFinder.java b/src/main/java/StevenDimDoors/experimental/decorators/DecoratorFinder.java new file mode 100644 index 0000000..ce925fe --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/decorators/DecoratorFinder.java @@ -0,0 +1,51 @@ +package StevenDimDoors.experimental.decorators; + +import java.util.ArrayList; +import java.util.Random; + +import StevenDimDoors.experimental.RoomData; + +public class DecoratorFinder +{ + private static ArrayList decorators = null; + + private DecoratorFinder() { } + + public static BaseDecorator find(RoomData room, Random random) + { + if (decorators == null) + { + load(); + } + + // Since there are only a few decorators right now, we just iterate + // over the list and check them all. If we add a lot, we'll need to + // switch to a more efficient approach. + ArrayList matches = new ArrayList(); + for (BaseDecorator decorator : decorators) + { + if (decorator.canDecorate(room)) + { + matches.add(decorator); + } + } + + if (matches.isEmpty()) + { + return null; + } + else + { + return matches.get( random.nextInt(matches.size()) ); + } + } + + private static void load() + { + // List all the decorators we have + decorators = new ArrayList(); + decorators.add(new LinkDestinationDecorator()); + decorators.add(new DefaultDoorDecorator()); + decorators.add(new TorchDecorator()); + } +} diff --git a/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java new file mode 100644 index 0000000..ffc4fa7 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java @@ -0,0 +1,23 @@ +package StevenDimDoors.experimental.decorators; + +import java.util.Random; + +import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.config.DDProperties; + +public class DefaultDoorDecorator extends BaseDecorator { + + @Override + public boolean canDecorate(RoomData room) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean decorate(RoomData room, Random random, + DDProperties properties) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java new file mode 100644 index 0000000..17f5db4 --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java @@ -0,0 +1,23 @@ +package StevenDimDoors.experimental.decorators; + +import java.util.Random; + +import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.config.DDProperties; + + +public class LinkDestinationDecorator extends BaseDecorator +{ + @Override + public boolean canDecorate(RoomData room) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean decorate(RoomData room, Random random, + DDProperties properties) { + // TODO Auto-generated method stub + return false; + } +} diff --git a/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java new file mode 100644 index 0000000..6d736ac --- /dev/null +++ b/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java @@ -0,0 +1,23 @@ +package StevenDimDoors.experimental.decorators; + +import java.util.Random; + +import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.config.DDProperties; + +public class TorchDecorator extends BaseDecorator { + + @Override + public boolean canDecorate(RoomData room) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean decorate(RoomData room, Random random, + DDProperties properties) { + // TODO Auto-generated method stub + return false; + } + +} -- 2.39.5 From f867f16d1d60258ddbc752f2d41ab55c0f244218 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Tue, 15 Apr 2014 01:46:39 -0400 Subject: [PATCH 20/24] Fixed Maze Bug Fixed a minor bug that affected maze decorators. --- src/main/java/StevenDimDoors/experimental/MazeBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index 592b2d0..96a7b6a 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -137,7 +137,7 @@ public class MazeBuilder links.addAll(room.getOutboundLinks()); // Protected rooms must be decorated because they have links. // Otherwise, choose randomly whether to decorate. - if (room.isProtected() && random.nextInt(MAX_DECORATION_CHANCE) < DECORATION_CHANCE) + if (room.isProtected() || random.nextInt(MAX_DECORATION_CHANCE) < DECORATION_CHANCE) { decorator = DecoratorFinder.find(room, random); if (decorator != null) -- 2.39.5 From be7cd9d1864008b76e7bfd9ddb18a6a8cc17a8ef Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Tue, 15 Apr 2014 04:30:31 -0400 Subject: [PATCH 21/24] More eye tweaks --- .../mod_pocketDim/ticking/MobMonolith.java | 16 +++++++++++----- .../mod_pocketDimClient/RenderMobObelisk.java | 16 +++++++++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java b/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java index d06ffac..6d18cb6 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/ticking/MobMonolith.java @@ -100,6 +100,12 @@ public class MobMonolith extends EntityFlying implements IMob { return false; } + + @Override + public float getEyeHeight() + { + return this.height +2F; + } public void setEntityPosition(Entity entity, double x, double y, double z) { @@ -206,14 +212,14 @@ public class MobMonolith extends EntityFlying implements IMob this.playSound(mod_pocketDim.modid+":monk", 1F, 1F); this.soundTime=100; } - if ((aggroPercent>.80)&&this.soundTime<100) + if ((aggroPercent>.70)&&this.soundTime<100) { - this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",2, 1F); - this.soundTime=200; + this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",1F, (float) (1+this.rand.nextGaussian())); + this.soundTime=100+this.rand.nextInt(75); } - if ((aggroPercent>.95)&&this.soundTime<200) + if ((aggroPercent>.90)&&this.soundTime<200) { - this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",5, 1F); + this.worldObj.playSoundEffect(entityPlayer.posX, entityPlayer.posY, entityPlayer.posZ,mod_pocketDim.modid+":tearing",7, 1F); this.soundTime=250; } this.soundTime--; diff --git a/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java b/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java index 48dbfa9..8cb4f97 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java +++ b/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderMobObelisk.java @@ -63,18 +63,28 @@ public class RenderMobObelisk extends RenderLiving GL11.glDisable(GL11.GL_DEPTH_TEST); GL11.glDepthMask(false); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + this.mainModel.onGround = this.renderSwingProgress(par1EntityLivingBase, par9); try { float interpolatedYaw = this.interpolateRotation(par1EntityLivingBase.prevRenderYawOffset, par1EntityLivingBase.renderYawOffset, par9); + float interpolatedYawHead = this.interpolateRotation(par1EntityLivingBase.prevRotationYawHead, par1EntityLivingBase.rotationYawHead, par9); + float rotation; + float pitch = par1EntityLivingBase.prevRotationPitch + (par1EntityLivingBase.rotationPitch - par1EntityLivingBase.prevRotationPitch) * par9; this.renderLivingAt(par1EntityLivingBase, x, y, z); - this.rotateCorpse(par1EntityLivingBase, 0, 0, par9); + + rotation = this.handleRotationFloat(par1EntityLivingBase, par9); + this.rotateCorpse(par1EntityLivingBase, rotation, interpolatedYaw, par9); + + float f6 = 0.0625F; GL11.glEnable(GL12.GL_RESCALE_NORMAL); GL11.glScalef(-1.0F, -1.0F, 1.0F); this.preRenderCallback(par1EntityLivingBase, par9); - GL11.glRotatef(interpolatedYaw , 0.0F, 1.0F, 0.0F); GL11.glRotatef(((MobMonolith)par1EntityLivingBase).pitchLevel , 1.0F, 0.0F, 0.0F); - this.renderModel(par1EntityLivingBase, 0, 0, 0,0, 0, 0); + + GL11.glTranslatef(0.0F, -24.0F * f6 - 0.0078125F, 0.0F); + + this.renderModel(par1EntityLivingBase, 0, 0, rotation, interpolatedYawHead - interpolatedYaw, pitch, f6); OpenGlHelper.setActiveTexture(OpenGlHelper.lightmapTexUnit); GL11.glDisable(GL11.GL_TEXTURE_2D); -- 2.39.5 From 77abcbb14811a5f0e569c89b91f0481f582ec3fc Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Tue, 15 Apr 2014 05:14:38 -0400 Subject: [PATCH 22/24] Progress on Maze Generation * Increased the chance of decorating unprotected rooms from 1/4 to 1/3. * Made minor optimizations to the box building function. * Added some important arguments to BaseDecorator.decorate(). Also updated all decorators as a result of this change. * Implemented TorchDecorator so that mazes have light sources in them. --- .../experimental/MazeBuilder.java | 12 +-- .../decorators/BaseDecorator.java | 4 +- .../decorators/DefaultDoorDecorator.java | 20 ++--- .../decorators/LinkDestinationDecorator.java | 19 ++--- .../decorators/TorchDecorator.java | 81 ++++++++++++++++--- 5 files changed, 102 insertions(+), 34 deletions(-) diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index 96a7b6a..c863046 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -17,7 +17,7 @@ public class MazeBuilder { private static final int POCKET_WALL_GAP = 4; private static final int DECORATION_CHANCE = 1; - private static final int MAX_DECORATION_CHANCE = 4; + private static final int MAX_DECORATION_CHANCE = 3; private MazeBuilder() { } @@ -142,11 +142,11 @@ public class MazeBuilder decorator = DecoratorFinder.find(room, random); if (decorator != null) { - decorator.decorate(room, random, properties); + decorator.decorate(room, world, offset, random, properties); } } } - // Iterate over all links plans and place links in the world + // Iterate over all link plans and place links in the world for (LinkPlan link : links) { // TODO: Add link placement code here! @@ -323,15 +323,15 @@ public class MazeBuilder } for (x = minX; x <= maxX; x++) { - for (y = minY; y <= maxY; y++) + for (y = minY + 1; y < maxY; y++) { setBlockDirectly(world, x, y, minZ, blockID, metadata); setBlockDirectly(world, x, y, maxZ, blockID, metadata); } } - for (z = minZ; z <= maxZ; z++) + for (z = minZ + 1; z < maxZ; z++) { - for (y = minY; y <= maxY; y++) + for (y = minY + 1; y < maxY; y++) { setBlockDirectly(world, minX, y, z, blockID, metadata); setBlockDirectly(world, maxX, y, z, blockID, metadata); diff --git a/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java index 05bd70f..2ea4e8a 100644 --- a/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java +++ b/src/main/java/StevenDimDoors/experimental/decorators/BaseDecorator.java @@ -2,7 +2,9 @@ package StevenDimDoors.experimental.decorators; import java.util.Random; +import net.minecraft.world.World; import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.config.DDProperties; public abstract class BaseDecorator @@ -11,5 +13,5 @@ public abstract class BaseDecorator public abstract boolean canDecorate(RoomData room); - public abstract boolean decorate(RoomData room, Random random, DDProperties properties); + public abstract void decorate(RoomData room, World world, Point3D offset, Random random, DDProperties properties); } diff --git a/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java index ffc4fa7..99446da 100644 --- a/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java +++ b/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java @@ -2,22 +2,24 @@ package StevenDimDoors.experimental.decorators; import java.util.Random; +import net.minecraft.world.World; + import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.config.DDProperties; -public class DefaultDoorDecorator extends BaseDecorator { - +public class DefaultDoorDecorator extends BaseDecorator +{ @Override - public boolean canDecorate(RoomData room) { - // TODO Auto-generated method stub - return false; + public boolean canDecorate(RoomData room) + { + return !room.getOutboundLinks().isEmpty(); } @Override - public boolean decorate(RoomData room, Random random, - DDProperties properties) { - // TODO Auto-generated method stub - return false; + public void decorate(RoomData room, World world, Point3D offset, Random random, DDProperties properties) + { + } } diff --git a/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java index 17f5db4..7203a44 100644 --- a/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java +++ b/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java @@ -2,22 +2,23 @@ package StevenDimDoors.experimental.decorators; import java.util.Random; -import StevenDimDoors.experimental.RoomData; -import StevenDimDoors.mod_pocketDim.config.DDProperties; +import net.minecraft.world.World; +import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.config.DDProperties; public class LinkDestinationDecorator extends BaseDecorator { @Override - public boolean canDecorate(RoomData room) { - // TODO Auto-generated method stub - return false; + public boolean canDecorate(RoomData room) + { + return room.getOutboundLinks().isEmpty() && !room.getInboundLinks().isEmpty(); } @Override - public boolean decorate(RoomData room, Random random, - DDProperties properties) { - // TODO Auto-generated method stub - return false; + public void decorate(RoomData room, World world, Point3D offset, Random random, DDProperties properties) + { + } } diff --git a/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java index 6d736ac..c200465 100644 --- a/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java +++ b/src/main/java/StevenDimDoors/experimental/decorators/TorchDecorator.java @@ -2,22 +2,85 @@ package StevenDimDoors.experimental.decorators; import java.util.Random; +import net.minecraft.block.Block; +import net.minecraft.util.MathHelper; +import net.minecraft.world.World; + +import StevenDimDoors.experimental.PartitionNode; import StevenDimDoors.experimental.RoomData; +import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.config.DDProperties; -public class TorchDecorator extends BaseDecorator { - +public class TorchDecorator extends BaseDecorator +{ @Override - public boolean canDecorate(RoomData room) { - // TODO Auto-generated method stub - return false; + public boolean canDecorate(RoomData room) + { + return !room.isProtected(); } @Override - public boolean decorate(RoomData room, Random random, - DDProperties properties) { - // TODO Auto-generated method stub - return false; + public void decorate(RoomData room, World world, Point3D offset, Random random, DDProperties properties) + { + // SenseiKiwi: Place a single random torch along the walls. + // We could do more complex arrangements but I feel that a single + // torches here and there will be a little unsettling. + // The walls might be broken by passages or decay, so this will + // require trial and error. + + final int MAX_ATTEMPTS = 5; + + int x; + int z; + int attempts = 0; + PartitionNode partition = room.getPartitionNode(); + int minX = partition.minCorner().getX() + offset.getX(); + int minZ = partition.minCorner().getZ() + offset.getZ(); + int maxX = partition.maxCorner().getX() + offset.getX(); + int maxZ = partition.maxCorner().getZ() + offset.getZ(); + int torchLevel = partition.minCorner().getY() + offset.getY() + 2; + + for (; attempts < MAX_ATTEMPTS; attempts++) + { + // Choose a random side of the room to place the torch. The sides are numbered arbitrarily here. + // Then choose a random position along the wall and check if there is a block there to place the + // torch against. We assume that all blocks are bricks and thus valid. + switch (random.nextInt(4)) + { + case 0: // Positive X side + z = MathHelper.getRandomIntegerInRange(random, minZ + 1, maxZ - 1); + if (!world.isAirBlock(maxX, torchLevel, z)) + { + world.setBlock(maxX - 1, torchLevel, z, Block.torchWood.blockID, 2, 0); + return; + } + break; + case 1: // Negative X side + z = MathHelper.getRandomIntegerInRange(random, minZ + 1, maxZ - 1); + if (!world.isAirBlock(minX, torchLevel, z)) + { + world.setBlock(minX + 1, torchLevel, z, Block.torchWood.blockID, 1, 0); + return; + } + break; + case 2: // Positive Z side + x = MathHelper.getRandomIntegerInRange(random, minX + 1, maxX - 1); + if (!world.isAirBlock(x, torchLevel, maxZ)) + { + world.setBlock(x, torchLevel, maxZ - 1, Block.torchWood.blockID, 4, 0); + return; + } + break; + case 3: // Negative Z side + x = MathHelper.getRandomIntegerInRange(random, minX + 1, maxX - 1); + if (!world.isAirBlock(x, torchLevel, minZ)) + { + world.setBlock(x, torchLevel, minZ + 1, Block.torchWood.blockID, 3, 0); + return; + } + break; + } + } } } -- 2.39.5 From 3664e707cf2c2b0b8f9234a50cb85a9b86f85217 Mon Sep 17 00:00:00 2001 From: StevenRS11 Date: Tue, 15 Apr 2014 07:27:08 -0400 Subject: [PATCH 23/24] Fixed minor issues Doors render right now No longer placing doors on left clicks --- .../mod_pocketDim/EventHookContainer.java | 6 +- .../mod_pocketDim/blocks/BaseDimDoor.java | 25 ++++++-- .../mod_pocketDimClient/RenderDimDoor.java | 63 ++++++++++--------- 3 files changed, 60 insertions(+), 34 deletions(-) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/EventHookContainer.java b/src/main/java/StevenDimDoors/mod_pocketDim/EventHookContainer.java index aa04337..9f7fd0c 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/EventHookContainer.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/EventHookContainer.java @@ -17,6 +17,7 @@ import net.minecraftforge.event.ForgeSubscribe; import net.minecraftforge.event.entity.living.LivingDeathEvent; import net.minecraftforge.event.entity.living.LivingFallEvent; import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action; import net.minecraftforge.event.terraingen.InitMapGenEvent; import net.minecraftforge.event.world.WorldEvent; import StevenDimDoors.mod_pocketDim.blocks.BaseDimDoor; @@ -84,7 +85,10 @@ public class EventHookContainer public void onPlayerEvent(PlayerInteractEvent event) { // Handle all door placement here - + if(event.action == Action.LEFT_CLICK_BLOCK) + { + return; + } World world = event.entity.worldObj; ItemStack stack = event.entityPlayer.inventory.getCurrentItem(); if (stack != null && stack.getItem() instanceof ItemDoor) diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java index 9c56e91..1b85af9 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/blocks/BaseDimDoor.java @@ -179,11 +179,30 @@ public abstract class BaseDimDoor extends BlockDoor implements IDimDoor, ITileEn if (tile instanceof TileEntityDimDoor) { TileEntityDimDoor dimTile = (TileEntityDimDoor) tile; - dimTile.openOrClosed = PocketManager.getLink(x, y, z, world.provider.dimensionId) != null; + dimTile.openOrClosed = this.isDoorOnRift(world, x, y, z); dimTile.orientation = this.getFullMetadata(world, x, y, z) & 7; } return this; } + + public boolean isDoorOnRift(World world, int x, int y, int z) + { + if(this.isUpperDoorBlock( world.getBlockMetadata(x, y, z))) + { + if(PocketManager.getLink(x, y, z, world.provider.dimensionId) != null||PocketManager.getLink(x, y-1, z, world.provider.dimensionId) != null) + { + return true; + } + } + else + { + if(PocketManager.getLink(x, y, z, world.provider.dimensionId) != null||PocketManager.getLink(x, y+1, z, world.provider.dimensionId) != null) + { + return true; + } + } + return false; + } /** * Is this block (a) opaque and (b) a full 1m cube? This determines whether or not to render the shared face of two @@ -192,9 +211,7 @@ public abstract class BaseDimDoor extends BlockDoor implements IDimDoor, ITileEn @Override public void updateTick(World par1World, int par2, int par3, int par4, Random par5Random) { - TileEntityDimDoor tile = (TileEntityDimDoor) par1World.getBlockTileEntity(par2, par3, par4); - tile.openOrClosed = this.isDoorOpen( par1World, par2, par3, par4); - tile.orientation = this.getFullMetadata(par1World, par2, par3, par4) & 7; + this.updateAttachedTile(par1World, par2, par3, par4); } @Override diff --git a/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderDimDoor.java b/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderDimDoor.java index 6639974..23e72bb 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderDimDoor.java +++ b/src/main/java/StevenDimDoors/mod_pocketDimClient/RenderDimDoor.java @@ -42,15 +42,7 @@ public class RenderDimDoor extends TileEntitySpecialRenderer public void renderDimDoorTileEntity(TileEntityDimDoor tile, double x, double y, double z) { - try - { - mod_pocketDim.dimensionalDoor.updateAttachedTile(tile.worldObj, - tile.xCoord, tile.yCoord, tile.zCoord); - } - catch (Exception e) - { - e.printStackTrace(); - } + // float playerX = (float)this.tileEntityRenderer.playerX; // float playerY = (float)this.tileEntityRenderer.playerY; @@ -215,62 +207,61 @@ public class RenderDimDoor extends TileEntitySpecialRenderer } GL11.glColor4d(var21 * var17, var22 * var17, var23 * var17, 1.0F); - if (tile.openOrClosed) - { + switch (tile.orientation) { case 0: - GL11.glVertex3d(x + .01F, y - 1, z); - GL11.glVertex3d(x + .01, y - 1, z + 1.0D); + GL11.glVertex3d(x + .01F, y, z); + GL11.glVertex3d(x + .01, y , z + 1.0D); GL11.glVertex3d(x + .01, y + 1, z + 1.0D); GL11.glVertex3d(x + .01, y + 1, z); break; case 1: GL11.glVertex3d(x, y + 1, z + .01); GL11.glVertex3d(x + 1, y + 1, z + .01); - GL11.glVertex3d(x + 1, y - 1, z + .01); - GL11.glVertex3d(x, y - 1, z + .01); + GL11.glVertex3d(x + 1, y , z + .01); + GL11.glVertex3d(x, y , z + .01); break; case 2: GL11.glVertex3d(x + .99, y + 1, z); GL11.glVertex3d(x + .99, y + 1, z + 1.0D); - GL11.glVertex3d(x + .99, y - 1, z + 1.0D); - GL11.glVertex3d(x + .99, y - 1, z); + GL11.glVertex3d(x + .99, y , z + 1.0D); + GL11.glVertex3d(x + .99, y , z); break; case 3: - GL11.glVertex3d(x, y - 1, z + .99); - GL11.glVertex3d(x + 1, y - 1, z + .99); + GL11.glVertex3d(x, y , z + .99); + GL11.glVertex3d(x + 1, y , z + .99); GL11.glVertex3d(x + 1, y + 1, z + .99); GL11.glVertex3d(x, y + 1, z + .99); break; case 4: - GL11.glVertex3d(x + .15F, y - 1, z); - GL11.glVertex3d(x + .15, y - 1, z + 1.0D); + GL11.glVertex3d(x + .15F, y , z); + GL11.glVertex3d(x + .15, y , z + 1.0D); GL11.glVertex3d(x + .15, y + 1, z + 1.0D); GL11.glVertex3d(x + .15, y + 1, z); break; case 5: GL11.glVertex3d(x, y + 1, z + .15); GL11.glVertex3d(x + 1, y + 1, z + .15); - GL11.glVertex3d(x + 1, y - 1, z + .15); - GL11.glVertex3d(x, y - 1, z + .15); + GL11.glVertex3d(x + 1, y , z + .15); + GL11.glVertex3d(x, y , z + .15); break; case 6: GL11.glVertex3d(x + .85, y + 1, z); GL11.glVertex3d(x + .85, y + 1, z + 1.0D); - GL11.glVertex3d(x + .85, y - 1, z + 1.0D); - GL11.glVertex3d(x + .85, y - 1, z); + GL11.glVertex3d(x + .85, y , z + 1.0D); + GL11.glVertex3d(x + .85, y , z); break; case 7: - GL11.glVertex3d(x, y - 1, z + .85); - GL11.glVertex3d(x + 1, y - 1, z + .85); + GL11.glVertex3d(x, y , z + .85); + GL11.glVertex3d(x + 1, y , z + .85); GL11.glVertex3d(x + 1, y + 1, z + .85); GL11.glVertex3d(x, y + 1, z + .85); break; } - } + GL11.glEnd(); @@ -299,7 +290,21 @@ public class RenderDimDoor extends TileEntitySpecialRenderer { if (properties.DoorRenderingEnabled) { - renderDimDoorTileEntity((TileEntityDimDoor) par1TileEntity, par2, par4, par6); + TileEntityDimDoor tile = (TileEntityDimDoor) par1TileEntity; + try + { + mod_pocketDim.dimensionalDoor.updateAttachedTile(tile.worldObj, + tile.xCoord, tile.yCoord, tile.zCoord); + } + catch (Exception e) + { + e.printStackTrace(); + } + + if (tile.openOrClosed) + { + renderDimDoorTileEntity((TileEntityDimDoor) par1TileEntity, par2, par4, par6); + } } } } -- 2.39.5 From fa629db4fec3df329bb482d3f9d43bfd4b3f1a1c Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Tue, 15 Apr 2014 07:40:44 -0400 Subject: [PATCH 24/24] Progress on Maze Generation * Implemented link creation based on link plans * Improvised an implementation of door placement in DefaultDoorDecorator for testing purposes. I'll provide a better version later. Known issues: 1. Doors with one-way links to other rooms will generate a return door at the destination. The return door doesn't actually lead back, it leads to a new pocket. Need to disable pair generation for doors in mazes. 2. Consider weighing sections by the door capacity to avoid putting a lot of doors into a small section. Also double-check that room selection within sections is unbiased. --- .../experimental/MazeBuilder.java | 37 ++++++++++++++++++- .../decorators/DefaultDoorDecorator.java | 26 ++++++++++++- .../decorators/LinkDestinationDecorator.java | 13 +++++++ .../StevenDimDoors/mod_pocketDim/Point3D.java | 7 ++++ 4 files changed, 80 insertions(+), 3 deletions(-) diff --git a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java index c863046..f7a434c 100644 --- a/src/main/java/StevenDimDoors/experimental/MazeBuilder.java +++ b/src/main/java/StevenDimDoors/experimental/MazeBuilder.java @@ -12,6 +12,10 @@ import StevenDimDoors.experimental.decorators.BaseDecorator; import StevenDimDoors.experimental.decorators.DecoratorFinder; import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.config.DDProperties; +import StevenDimDoors.mod_pocketDim.core.DimLink; +import StevenDimDoors.mod_pocketDim.core.LinkTypes; +import StevenDimDoors.mod_pocketDim.core.NewDimData; +import StevenDimDoors.mod_pocketDim.core.PocketManager; public class MazeBuilder { @@ -146,10 +150,39 @@ public class MazeBuilder } } } + // Iterate over all link plans and place links in the world - for (LinkPlan link : links) + NewDimData dimension = PocketManager.getDimensionData(world); + for (LinkPlan plan : links) { - // TODO: Add link placement code here! + createLinkFromPlan(plan, dimension, world); + } + } + + private static void createLinkFromPlan(LinkPlan plan, NewDimData dimension, World world) + { + // TODO: Support entrances! Right now we'll treat them as dungeon doors for testing + + DimLink link; + Point3D source; + Point3D destination; + int orientation; + + source = plan.sourcePoint(); + orientation = world.getBlockMetadata(source.getX(), source.getY(), source.getZ()) & 3; + + // Check the link type and set the destination accordingly + if (plan.isInternal()) + { + // Create a link between sections + destination = plan.destinationPoint(); + link = dimension.createLink(source.getX(), source.getY(), source.getZ(), LinkTypes.DUNGEON, orientation); + dimension.setDestination(link, destination.getX(), destination.getY(), destination.getZ()); + } + else + { + // Create a dungeon link + dimension.createLink(source.getX(), source.getY(), source.getZ(), LinkTypes.DUNGEON, orientation); } } diff --git a/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java index 99446da..fd653ab 100644 --- a/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java +++ b/src/main/java/StevenDimDoors/experimental/decorators/DefaultDoorDecorator.java @@ -2,10 +2,12 @@ package StevenDimDoors.experimental.decorators; import java.util.Random; +import net.minecraft.item.ItemDoor; import net.minecraft.world.World; - +import StevenDimDoors.experimental.LinkPlan; import StevenDimDoors.experimental.RoomData; import StevenDimDoors.mod_pocketDim.Point3D; +import StevenDimDoors.mod_pocketDim.mod_pocketDim; import StevenDimDoors.mod_pocketDim.config.DDProperties; public class DefaultDoorDecorator extends BaseDecorator @@ -19,6 +21,28 @@ public class DefaultDoorDecorator extends BaseDecorator @Override public void decorate(RoomData room, World world, Point3D offset, Random random, DDProperties properties) { + // TODO: This is just an improvised implementation for testing + Point3D corner = room.getPartitionNode().minCorner().clone(); + corner.add(offset); + + int count = 0; + Point3D source = null; + for (LinkPlan plan : room.getOutboundLinks()) + { + source = new Point3D(corner.getX() + 2, corner.getY() + 2, corner.getZ() + count + 1); + ItemDoor.placeDoorBlock(world, source.getX(), source.getY() - 1, source.getZ(), 0, mod_pocketDim.dimensionalDoor); + plan.setSourcePoint(source); + count++; + } + + if (source == null) + { + throw new IllegalStateException("This should never happen because this decorator only applies if outbound links exist!"); + } + for (LinkPlan plan : room.getInboundLinks()) + { + plan.setDestinationPoint(source); + } } diff --git a/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java b/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java index 7203a44..0b5d21f 100644 --- a/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java +++ b/src/main/java/StevenDimDoors/experimental/decorators/LinkDestinationDecorator.java @@ -4,6 +4,8 @@ import java.util.Random; import net.minecraft.world.World; +import StevenDimDoors.experimental.LinkPlan; +import StevenDimDoors.experimental.PartitionNode; import StevenDimDoors.experimental.RoomData; import StevenDimDoors.mod_pocketDim.Point3D; import StevenDimDoors.mod_pocketDim.config.DDProperties; @@ -19,6 +21,17 @@ public class LinkDestinationDecorator extends BaseDecorator @Override public void decorate(RoomData room, World world, Point3D offset, Random random, DDProperties properties) { + // Set the center of the room as the destination for all inbound links + PartitionNode partition = room.getPartitionNode(); + Point3D destination = partition.minCorner().clone(); + destination.add( + offset.getX() + partition.width() / 2, + offset.getY() + 2, + offset.getZ() + partition.length() / 2); + for (LinkPlan plan : room.getInboundLinks()) + { + plan.setDestinationPoint(destination); + } } } diff --git a/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java b/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java index 627bdab..f03469a 100644 --- a/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java +++ b/src/main/java/StevenDimDoors/mod_pocketDim/Point3D.java @@ -62,6 +62,13 @@ public class Point3D implements Serializable { this.y += y; this.z += z; } + + public void add(Point3D other) + { + this.x += other.x; + this.y += other.y; + this.z += other.z; + } @Override public Point3D clone() -- 2.39.5