public NeighborCell(int x, int y, int z, CellEdge edge) { X = x; Y = y; Z = z; SharedEdge = edge; }
private void InitializeEdges() { //Initialize all edges for (int x = 0; x < mEdgesX.GetLength(0); x++) { for (int y = 0; y < mEdgesX.GetLength(1); y++) { for (int z = 0; z < mEdgesX.GetLength(2); z++) { CellEdge e = new CellEdge(x, y, z, CellEdgeAlignment.X); mEdges.Add(e); mEdgesX[x, y, z] = e; } } } for (int x = 0; x < mEdgesY.GetLength(0); x++) { for (int y = 0; y < mEdgesY.GetLength(1); y++) { for (int z = 0; z < mEdgesY.GetLength(2); z++) { CellEdge e = new CellEdge(x, y, z, CellEdgeAlignment.Y); mEdges.Add(e); mEdgesY[x, y, z] = e; } } } for (int x = 0; x < mEdgesZ.GetLength(0); x++) { for (int y = 0; y < mEdgesZ.GetLength(1); y++) { for (int z = 0; z < mEdgesZ.GetLength(2); z++) { CellEdge e = new CellEdge(x, y, z, CellEdgeAlignment.Z); mEdges.Add(e); mEdgesZ[x, y, z] = e; } } } }
/// <summary> /// Finds neighboring cells around a node which are not occupied by other nodes. /// </summary> /// <param name="grid">Cell grid</param> /// <param name="node">Node to search around</param> /// <param name="results">Vacant cells found</param> private void FindVacantNeighborCells(Node[,,] grid, Node node, List <NeighborCell> results) { //North edges if (node.PositionY > 0) { for (int i = node.PositionX; i < node.PositionX + node.Width; i++) { CellEdge e = mEdgesY[i, node.PositionY - 1, node.PositionZ]; results.Add(new NeighborCell(i, node.PositionY - 1, node.PositionZ, e)); } } //South edges if (node.PositionY + node.Length - 1 < mEdgesY.GetLength(1)) { for (int i = node.PositionX; i < node.PositionX + node.Width; i++) { CellEdge e = mEdgesY[i, node.PositionY - 1 + node.Length, node.PositionZ]; results.Add(new NeighborCell(i, node.PositionY + node.Length, node.PositionZ, e)); } } //West edges if (node.PositionX > 0) { for (int i = node.PositionY; i < node.PositionY + node.Length; i++) { CellEdge e = mEdgesX[node.PositionX - 1, i, node.PositionZ]; results.Add(new NeighborCell(node.PositionX - 1, i, node.PositionZ, e)); } } //East edges if (node.PositionX + node.Width - 1 < mEdgesX.GetLength(0)) { for (int i = node.PositionY; i < node.PositionY + node.Length; i++) { CellEdge e = mEdgesX[node.PositionX - 1 + node.Width, i, node.PositionZ]; results.Add(new NeighborCell(node.PositionX + node.Width, i, node.PositionZ, e)); } } //Down edges if (node.PositionZ > 0) { for (int ix = node.PositionX; ix < node.PositionX + node.Width; ix++) { for (int iy = node.PositionY; iy < node.PositionY + node.Length; iy++) { CellEdge e = mEdgesZ[ix, iy, node.PositionZ - 1]; results.Add(new NeighborCell(ix, iy, node.PositionZ - 1, e)); } } } //Up edges if (node.PositionZ < mEdgesZ.GetLength(2)) { for (int ix = node.PositionX; ix < node.PositionX + node.Width; ix++) { for (int iy = node.PositionY; iy < node.PositionY + node.Length; iy++) { CellEdge e = mEdgesZ[ix, iy, node.PositionZ]; results.Add(new NeighborCell(ix, iy, node.PositionZ + 1, e)); } } } // Removed occupied cells for (int i = results.Count - 1; i >= 0; i--) { if (grid[results[i].X, results[i].Y, results[i].Z] != null) { results.RemoveAt(i); } } }
/// <summary> /// Generates a seeded randomized dungeon /// </summary> /// <param name="seed"></param> public void Generate(int seed) { mNodes = new List <Node>(Width * Length * Floors); mEdges = new List <CellEdge>(Width * Length * Floors * 3 - Width - Length - Floors); InitializeEdges(); Random rnd = new Random(seed); //Use this list for fetching walls along nodes and picking node walls at random List <CellEdge> nodeWalls = new List <CellEdge>(); List <NeighborCell> neighborCells = new List <NeighborCell>(); //Backtracking stack List <Node> nodeStack = new List <Node>(); //Also, remember which 'cells' are occupied, and which node //Since a node can vary in size, it may occupy multiple cells Node[,,] nodeGrid = new Node[Width, Length, Floors]; //Create start node Node startNode = new Node(rnd.Next(Width), rnd.Next(Length), rnd.Next(Floors), 1, 1); GetCellEdges(startNode, nodeWalls); SetEdges(nodeWalls, CellEdgeType.Unused, CellEdgeType.Wall); PlaceNodeOnGrid(nodeGrid, startNode); mNodes.Add(startNode); Node currentNode = startNode; bool mazeFilled = false; while (!mazeFilled) { neighborCells.Clear(); nodeWalls.Clear(); FindVacantNeighborCells(nodeGrid, currentNode, neighborCells); if (neighborCells.Count > 0) { //Pick an empty space NeighborCell target = neighborCells[rnd.Next(neighborCells.Count)]; //...create an opening to this new space CellEdge openEdge = target.SharedEdge; openEdge.ConnectionType = CellEdgeType.Door; //...and create another node on the other side Node newNode = null; while (newNode == null) { NodeConfiguration size = PickRandomNodeConfiguration(rnd); newNode = TryNodePlacement(nodeGrid, rnd, target.X, target.Y, target.Z, size.Width, size.Length); } GetCellEdges(newNode, nodeWalls); SetEdges(nodeWalls, CellEdgeType.Unused, CellEdgeType.Wall); //Move into the new room, push old room onto stack nodeStack.Add(currentNode); currentNode = newNode; } else { //Else, pop back to previous room, and find new walls if (nodeStack.Count > 0) { currentNode = nodeStack[nodeStack.Count - 1]; nodeStack.RemoveAt(nodeStack.Count - 1); } else { //If there are no new places to go to, the maze is filled mazeFilled = true; } } } Console.WriteLine("Created " + mNodes.Count + " node dungeon."); }