private void FillEmptyCellsWithWalls(RandomDungeon dungeon) { for (int x = 0; x < dungeon.width; ++x) { for (int y = 0; y < dungeon.height; ++y) { if (dungeon.TileType(x, y) == RandomDungeonTileData.EMPTY_TILE) { dungeon.ChangeTileType(x, y, RandomDungeonTileData.WALL_TILE); } } } }
private void EnforceWallHeight(RandomDungeon dungeon) { // Add a bottom layer of walls for (int x = 0; x < dungeon.width; ++x) { for (int y = dungeon.height - 2; y >= 0; --y) { if (dungeon.TileType(x, y) == RandomDungeonTileData.WALL_TILE && dungeon.TileType(x, y + 1) == RandomDungeonTileData.EMPTY_TILE && (y - 1 < 0 || dungeon.TileType(x, y - 1) != RandomDungeonTileData.WALL_TILE)) { dungeon.ChangeTileType(x, y + 1, RandomDungeonTileData.WALL_TILE); } } } for (int x = 0; x < dungeon.width; ++x) { for (int y = dungeon.height - 2; y >= 0; --y) { if (dungeon.TileType(x, y) == RandomDungeonTileData.EMPTY_TILE && (y - 1 < 0 || dungeon.TileType(x, y - 1) == RandomDungeonTileData.WALL_TILE) && ((x - 1 >= 0 && dungeon.TileType(x - 1, y) == RandomDungeonTileData.WALL_TILE) || (x + 1 < dungeon.width && dungeon.TileType(x + 1, y) == RandomDungeonTileData.WALL_TILE))) { dungeon.ChangeTileType(x, y + 1, RandomDungeonTileData.WALL_TILE); } } } for (int x = 0; x < dungeon.width; ++x) { for (int y = 0; y < dungeon.height; ++y) { int tile = dungeon.TileType(x, y); bool wallBelow = (y < dungeon.height - 1 && dungeon.TileType(x, y + 1) == RandomDungeonTileData.WALL_TILE); if (tile == RandomDungeonTileData.EMPTY_TILE) { if (wallBelow) { dungeon.ChangeTileType(x, y, RandomDungeonTileData.WALL_TILE); } } } } }
/// <summary> /// Given the current progress of map generation, generates a random dungeon. /// This is only exposed as public for demonstration purposes when we want to show the /// dungeon being generated dynamically. /// </summary> /// <param name="generationData"></param> /// <returns></returns> public RandomDungeon GenerateRandomDungeonFromCurrentProgress(RandomDungeonGenerationData generationData, Dictionary <Vector2Int, RandomDungeonTileData> scratch = null) { RandomDungeon dungeon = GenerateDungeonObjectFromScratchPad(scratch != null ? scratch : mRoomScratch); CloseExitsToNowhere(dungeon); if (generationData.enforceTwoTileHighWalls) { EnforceWallHeight(dungeon); } if (generationData.fillEmptyCellsWithWalls) { FillEmptyCellsWithWalls(dungeon); } return(dungeon); }
/// <summary> /// After map generation is finished, this will close off all the exits that don't lead to /// another room. /// </summary> /// <param name="dungeon"></param> private void CloseExitsToNowhere(RandomDungeon dungeon) { if (stopCloseExitsToNowhere) { return; } int exitPasses = 2; // should be equal the wall height for (int exitPass = 0; exitPass < exitPasses; ++exitPass) { for (int x = 0; x < dungeon.width; ++x) { for (int y = 0; y < dungeon.height; ++y) { int tile = dungeon.TileType(x, y); if (tile == RandomDungeonTileData.EXIT_TILE) { if (dungeon.NumWalkableNeighbors(x, y) <= 1) { dungeon.ChangeTileType(x, y, RandomDungeonTileData.WALL_TILE); } } } } } for (int x = 0; x < dungeon.width; ++x) { for (int y = 0; y < dungeon.height; ++y) { int tile = dungeon.TileType(x, y); if (tile == RandomDungeonTileData.EMPTY_TILE) { if (dungeon.NumWalkableNeighbors(x, y) >= 1) { dungeon.ChangeTileType(x, y, RandomDungeonTileData.WALL_TILE); } } } } }
/// <summary> /// Generates the RandomDungeon object from the scratchpad, creating the final 2d array that /// represents the dungeon in the process. /// </summary> /// <returns></returns> private RandomDungeon GenerateDungeonObjectFromScratchPad(Dictionary <Vector2Int, RandomDungeonTileData> scratch) { int minX = int.MaxValue, minY = int.MaxValue, maxX = int.MinValue, maxY = int.MinValue; foreach (Vector2Int pos in scratch.Keys) { minX = Mathf.Min(minX, pos.x); minY = Mathf.Min(minY, pos.y); maxX = Mathf.Max(maxX, pos.x); maxY = Mathf.Max(maxY, pos.y); } int padding = 2; int width = maxX - minX + 1 + padding * 2; int height = maxY - minY + 1 + padding * 2; RandomDungeon dungeon = new RandomDungeon(width, height); foreach (KeyValuePair <Vector2Int, RandomDungeonTileData> pair in scratch) { int x = pair.Key.x - minX + padding; int y = pair.Key.y - minY + padding; dungeon.SetData(x, y, pair.Value); } for (int i = 0; i < mPrimaryPathPositions.Count; ++i) { mPrimaryPathPositions[i] += (new Vector2Int(-minX, -minY) + new Vector2Int(padding, padding)); } dungeon.primaryPathPositions = mPrimaryPathPositions; return(dungeon); }
public void EvaluateDungeon(RandomDungeon dungeon) { int[] orthogonalOffsets = new int[] { 0, 1, 1, 0, 0, -1, -1, 0 }; for (int x = 0; x < dungeon.width; ++x) { for (int y = 0; y < dungeon.height; ++y) { // Only evaluate walkable tiles if (dungeon.TileType(x, y) != RandomDungeonTileData.WALKABLE_TILE && dungeon.TileType(x, y) != RandomDungeonTileData.EXIT_TILE) { continue; } RandomDungeonTileData data = dungeon.Data(x, y); // If the room isn't already part of the set, add it RandomDungeonNetworkNode node = null; if (!mNodeMap.TryGetValue(data.room, out node)) { node = new RandomDungeonNetworkNode(); node.roomId = data.room; node.distanceFromPrimaryPath = data.distanceFromPrimaryPath; mNodeMap.Add(data.room, node); } // Distance from primary path should be the closest tile that we can get into for this room. node.distanceFromPrimaryPath = Mathf.Min(data.distanceFromPrimaryPath, node.distanceFromPrimaryPath); // Evaluate connections (orthogonally, only considering walkable / exit tiles) for (int i = 0; i < orthogonalOffsets.Length; i += 2) { int neighborX = x + orthogonalOffsets[i]; int neighborY = y + orthogonalOffsets[i + 1]; if (dungeon.IsPositionInBounds(neighborX, neighborY)) { RandomDungeonTileData neighborData = dungeon.Data(neighborX, neighborY); bool isWalkable = (neighborData.tileType == RandomDungeonTileData.WALKABLE_TILE || neighborData.tileType == RandomDungeonTileData.EXIT_TILE); if (isWalkable && neighborData.room != node.roomId && !node.ConnectionExists(neighborData.room)) { node.AddConnection(neighborData.room); } } } // Keep track of all walkable tiles in this room so we know where we can spawn stuff. // todo bdsowers - change the name of this // By design it only keeps track of tiles that aren't next to a wall, but that // isn't conveyed in the name. // todo bdsowers - move a 'num surrounding walls' into map generation, as that can // be useful for more than just this. bool hasUnwalkableNeighbor = false; for (int offsetX = -1; offsetX <= 1; ++offsetX) { for (int offsetY = -1; offsetY <= 1; ++offsetY) { int testX = x + offsetX; int testY = y + offsetY; if (!dungeon.IsPositionInBounds(testX, testY)) { hasUnwalkableNeighbor = true; continue; } char tileType = dungeon.TileType(testX, testY); if (tileType != RandomDungeonTileData.WALKABLE_TILE && tileType != RandomDungeonTileData.EXIT_TILE) { hasUnwalkableNeighbor = true; continue; } } } if (!hasUnwalkableNeighbor) { node.RegisterEmptyPosition(x, y); } } } }