private TileMap ExcavateRooms(TileMap chunk, LabyrinthDungeon dungeon, TextWriter logStream) { logStream.WriteLine("Excavating room..."); logStream.WriteLine(dungeon.Rooms.Count); // Fill tiles with corridor values for each room in dungeon. foreach (var room in dungeon.Rooms) { // Get the room min and max location in tile coordinates. var right = room.Bounds.X + room.Bounds.Width - 1; var bottom = room.Bounds.Y + room.Bounds.Height - 1; var minPoint = new Vector2I(room.Bounds.X * 2 + 1, room.Bounds.Y * 2 + 1); var maxPoint = new Vector2I(right * 2, bottom * 2); // Fill the room in tile space with an empty value. for (var row = minPoint.Y; row <= maxPoint.Y; row++) { for (var column = minPoint.X; column <= maxPoint.X; column++) { ExcavateChunkPoint(chunk, new Vector2I(column, row)); } } } logStream.WriteLine("Room complete!"); return(chunk); }
public void PlaceRooms(LabyrinthDungeon dungeon) { if ((NumRooms <= 0) || (MinRoomRows <= 0) || (MaxRoomRows <= 0) || (MinRoomColumns <= 0) || (MaxRoomColumns <= 0)) { throw new InvalidOperationException("Invalid object state; all properties must have positive values."); } // Loop for the number of rooms to place: for (var roomCounter = 0; roomCounter < NumRooms; roomCounter++) { var room = CreateRoom(); var bestRoomPlacementScore = int.MaxValue; Vector2I?bestRoomPlacementLocation = null; foreach (var currentRoomPlacementLocation in dungeon.CorridorCellLocations) { var currentRoomPlacementScore = CalculateRoomPlacementScore(currentRoomPlacementLocation, dungeon, room); if (currentRoomPlacementScore < bestRoomPlacementScore) { bestRoomPlacementScore = currentRoomPlacementScore; bestRoomPlacementLocation = currentRoomPlacementLocation; } } // Create room at best room placement cell. if (bestRoomPlacementLocation.HasValue) { PlaceRoom(bestRoomPlacementLocation.Value, dungeon, room); } } PlaceDoors(dungeon); }
private LabyrinthDungeon CreateDenseMaze(int rows, int columns, double changeDirectionModifier) { var map = new LabyrinthDungeon(rows, columns); map.MarkCellsUnvisited(); var currentLocation = map.PickRandomCellAndMarkItVisited(_random); var previousDirection = Direction.North; while (!map.AllCellsVisited) { var directionPicker = new DirectionPicker(_random, previousDirection, changeDirectionModifier); var direction = directionPicker.GetNextDirection(); while (!map.HasAdjacentCellInDirection(currentLocation, direction) || map.AdjacentCellInDirectionIsVisited(currentLocation, direction)) { if (directionPicker.HasNextDirection) { direction = directionPicker.GetNextDirection(); } else { currentLocation = map.GetRandomVisitedCell(currentLocation, _random); // get a new previously visited location directionPicker = new DirectionPicker(_random, previousDirection, changeDirectionModifier); // reset the direction picker direction = directionPicker.GetNextDirection(); // get a new direction. } } currentLocation = map.CreateCorridor(currentLocation, direction); map.FlagCellAsVisited(currentLocation); previousDirection = direction; } return(map); }
/// <param name="sparsenessFactor">Percentage of the map (0.0 to 1.0) turned to walls.</param> private void SparsifyMaze(LabyrinthDungeon map, double sparsenessFactor) { // Calculate the number of cells to remove as a percentage of the total number of cells in the map: var noOfDeadEndCellsToRemove = (int)System.Math.Ceiling(sparsenessFactor * map.Rows * map.Columns); var points = map.DeadEndCellLocations; for (var i = 0; i < noOfDeadEndCellsToRemove; i++) { if (points.Count == 0) { // check if there is another item in our enumerator points = map.DeadEndCellLocations; // get a new list if (points.Count == 0) { break; // no new items exist so break out of loop } } var index = _random.Next(0, points.Count); var point = points[index]; points.RemoveAt(index); if (map[point].IsDeadEnd) { // make sure the status of the cell hasn't change map.CreateWall(point, map[point].CalculateDeadEndCorridorDirection()); } } }
private int CalculateRoomPlacementScore(Vector2I location, LabyrinthDungeon dungeon, Room room) { // Check if the room at the given location will fit inside the bounds of the map. if (Contains(dungeon.Bounds, location, room)) { var roomPlacementScore = 0; // Loop for each cell in the room. for (var column = 0; column < room.Columns; column++) { for (var row = 0; row < room.Rows; row++) { // Translate the room cell location to its location in the dungeon. var dungeonLocation = location + new Vector2I(column, row); // Add 1 point for each adjacent corridor to the cell. if (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.North)) { roomPlacementScore++; } if (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.South)) { roomPlacementScore++; } if (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.West)) { roomPlacementScore++; } if (dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.East)) { roomPlacementScore++; } // Add 3 points if the cell overlaps an existing corridor. if (dungeon[dungeonLocation].IsCorridor) { roomPlacementScore += 3; } // Add 100 points if the cell overlaps any existing room cells. foreach (var dungeonRoom in dungeon.Rooms) { if (dungeonRoom.Bounds.Contains(dungeonLocation)) { roomPlacementScore += 100; } } } } return(roomPlacementScore); } else { return(int.MaxValue); } }
private void PlaceDoors(LabyrinthDungeon dungeon) { foreach (var room in dungeon.Rooms) { var hasNorthDoor = false; var hasSouthDoor = false; var hasWestDoor = false; var hasEastDoor = false; // TODO: Convert this into 4 loops on the sides of the room. for (var row = 0; row < room.Rows; row++) { for (var column = 0; column < room.Columns; column++) { var cellLocation = new Vector2I(column, row); // Translate the room cell location to its location in the dungeon: var dungeonLocation = new Vector2I(room.Bounds.X, room.Bounds.Y) + cellLocation; // Check if we are on the west boundary of our roomand if there is a corridor to the west: if (!hasWestDoor && (cellLocation.X == 0) && dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.West)) { dungeon.CreateDoor(dungeonLocation, Direction.West); hasWestDoor = true; } // Check if we are on the east boundary of our room and if there is a corridor to the east if (!hasEastDoor && (cellLocation.X == room.Columns - 1) && dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.East)) { dungeon.CreateDoor(dungeonLocation, Direction.East); hasEastDoor = true; } // Check if we are on the north boundary of our room and if there is a corridor to the north if (!hasNorthDoor && (cellLocation.Y == 0) && dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.North)) { dungeon.CreateDoor(dungeonLocation, Direction.North); hasNorthDoor = true; } // Check if we are on the south boundary of our room and if there is a corridor to the south if (!hasSouthDoor && (cellLocation.Y == room.Rows - 1) && dungeon.AdjacentCellInDirectionIsCorridor(dungeonLocation, Direction.South)) { dungeon.CreateDoor(dungeonLocation, Direction.South); hasSouthDoor = true; } } } } }
private TileMap ExcavateCorridors(TileMap chunk, LabyrinthDungeon dungeon) { // Loop for each corridor cell and expand it. foreach (var cellLocation in dungeon.CorridorCellLocations) { var tileLocation = new Vector2I(cellLocation.X * 2 + 1, cellLocation.Y * 2 + 1); ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X, tileLocation.Y)); if (dungeon[cellLocation].NorthSide == SideType.Empty) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X, tileLocation.Y - 1)); } else if (dungeon[cellLocation].NorthSide == SideType.Door) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X, tileLocation.Y - 1)); chunk[tileLocation.Y - 1, tileLocation.X] = TileRegistry.Door; } if (dungeon[cellLocation].SouthSide == SideType.Empty) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X, tileLocation.Y + 1)); } else if (dungeon[cellLocation].SouthSide == SideType.Door) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X, tileLocation.Y + 1)); chunk[tileLocation.Y + 1, tileLocation.X] = TileRegistry.Door; } if (dungeon[cellLocation].WestSide == SideType.Empty) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X - 1, tileLocation.Y)); } else if (dungeon[cellLocation].WestSide == SideType.Door) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X - 1, tileLocation.Y)); chunk[tileLocation.Y, tileLocation.X - 1] = TileRegistry.Door; } if (dungeon[cellLocation].EastSide == SideType.Empty) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X + 1, tileLocation.Y)); } else if (dungeon[cellLocation].EastSide == SideType.Door) { ExcavateChunkPoint(chunk, new Vector2I(tileLocation.X + 1, tileLocation.Y)); chunk[tileLocation.Y, tileLocation.X + 1] = TileRegistry.Door; } } return(chunk); }
private TileMap ConvertToTileMap(TileMap chunk, LabyrinthDungeon dungeon, TextWriter logStream) { logStream.WriteLine("Converting dungeon into chunk..."); logStream.Write("Generating a rocky chunk..."); chunk = GenerateRockyChunk(chunk); logStream.WriteLine(" done!"); logStream.Write("Excavating rooms..."); chunk = ExcavateRooms(chunk, dungeon, logStream); logStream.WriteLine(" done!"); logStream.Write("Excavating corridors..."); chunk = ExcavateCorridors(chunk, dungeon); logStream.WriteLine(" done!"); return(chunk); }
/// <param name="deadEndRemovalModifier">Percentage value (0.0 - 1.0) of dead ends to convert into loops.</param> private void RemoveDeadEnds(LabyrinthDungeon map, double deadEndRemovalModifier) { var noOfDeadEndCellsToRemove = (int)System.Math.Ceiling(deadEndRemovalModifier * map.Rows * map.Columns); var deadEndLocations = map.DeadEndCellLocations; for (var i = 0; i < noOfDeadEndCellsToRemove; i++) { if (deadEndLocations.Count == 0) { break; } var index = _random.Next(0, deadEndLocations.Count); var deadEndLocation = deadEndLocations[index]; deadEndLocations.RemoveAt(index); if (map[deadEndLocation].IsDeadEnd) { var currentLocation = deadEndLocation; do { // Initialize the direction picker not to select the dead-end corridor direction. var directionPicker = new DirectionPicker(_random, map[currentLocation].CalculateDeadEndCorridorDirection(), 1); var direction = directionPicker.GetNextDirection(); while (!map.HasAdjacentCellInDirection(currentLocation, direction)) { if (directionPicker.HasNextDirection) { direction = directionPicker.GetNextDirection(); } else { throw new InvalidOperationException("This should not happen."); } } // Create a corridor in the selected direction: currentLocation = map.CreateCorridor(currentLocation, direction); }while (map[currentLocation].IsDeadEnd); // stop when you intersect an existing corridor } } }
private void PlaceRoom(Vector2I location, LabyrinthDungeon dungeon, Room room) { // Offset the room origin to the new location. room.SetLocation(location); // Loop for each cell in the room for (var row = 0; row < room.Rows; row++) { for (var column = 0; column < room.Columns; column++) { // Translate the room cell location to its location in the dungeon. var dungeonLocation = location + new Vector2I(column, row); dungeon[dungeonLocation].NorthSide = room[row, column].NorthSide; dungeon[dungeonLocation].SouthSide = room[row, column].SouthSide; dungeon[dungeonLocation].WestSide = room[row, column].WestSide; dungeon[dungeonLocation].EastSide = room[row, column].EastSide; // TODO: This part may be unnecessary. // Create room walls on map (either side of the wall) if ((column == 0) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, Direction.West))) { dungeon.CreateWall(dungeonLocation, Direction.West); } if ((column == room.Columns - 1) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, Direction.East))) { dungeon.CreateWall(dungeonLocation, Direction.East); } if ((row == 0) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, Direction.North))) { dungeon.CreateWall(dungeonLocation, Direction.North); } if ((row == room.Rows - 1) && (dungeon.HasAdjacentCellInDirection(dungeonLocation, Direction.South))) { dungeon.CreateWall(dungeonLocation, Direction.South); } } } dungeon.Rooms.Add(room); }