/// Randomly turns some [wall] tiles into [floor] and vice versa. private void Erode(int iterations, TileType floor = TileType.Floor, TileType wall = TileType.Wall) { var bounds = Level.Bounds.Deflate(); for (var i = 0; i < iterations; i++) { // TODO: This way this works is super inefficient. Would be better to // keep track of the floor tiles near open ones and choose from them. var pos = bounds.PickRandom(); if (GetTile(pos) != wall) { continue; } // Keep track of how many floors we're adjacent too. We will only erode // if we are directly next to a floor. var floors = Direction.All.Count(dir => GetTile(pos + dir) == floor); // Prefer to erode tiles near more floor tiles so the erosion isn't too // spiky. if (floors < 2) { continue; } if (Randomizer.ChanceIn(9 - floors)) { SetTile(pos, floor); } } }
private void AddJunction(Vector pos) { var tile = Randomizer.ChanceIn(4) ? (Randomizer.ChanceIn(3) ? TileType.OpenDoor : TileType.Floor) : TileType.ClosedDoor; SetTile(pos, tile); }
private void ConnectRegions() { // Find all of the tiles that can connect two (or more) regions. var connectorRegions = new Dictionary <Vector, HashSet <int> >(); foreach (var pos in Bounds.Deflate()) { // Can't already be part of a region. if (GetTile(pos) != TileType.Wall) { continue; } var regions = new HashSet <int>(); var neighbours = Direction.Cardinal .Select(dir => pos + dir) .Where(p => Bounds.Contains(p)); foreach (var neighbour in neighbours) { regions.Add(regionMap[neighbour.X, neighbour.Y]); } if (regions.Count < 2) { continue; } connectorRegions[pos] = regions; } var connectors = connectorRegions.Keys.ToList(); // Keep track of which regions have been merged. This maps an original // region index to the one it has been merged to. var merged = new Dictionary <int, int>(); var openRegions = new HashSet <int>(); for (var i = 0; i <= currentRegion; i++) { merged[i] = i; openRegions.Add(i); } // Keep connecting regions until we're down to one. while (openRegions.Count > 1) { var connector = connectors.PickRandom(); if (connector == null) { break; } // Carve the connection. AddJunction(connector); // Merge the connected regions. We'll pick one region (arbitrarily) and // map all of the other regions to its index. var mRegions = connectorRegions[connector] .Select(region => merged[region]).ToList(); var dest = mRegions.First(); var sources = mRegions.Skip(1).ToList(); // Merge all of the affected regions. We have to look at *all* of the // regions because other regions may have previously been merged with // some of the ones we're merging now. for (var i = 0; i <= currentRegion; i++) { if (sources.Contains(merged[i])) { merged[i] = dest; } } // The sources are no longer in use. openRegions.RemoveWhere(i => sources.Contains(i)); // Remove any connectors that aren't needed anymore. connectors.RemoveAll(pos => { // Don't allow connectors right next to each other. if ((connector - pos).Distance < 2) { return(true); } // If the connector no long spans different regions, we don't need it. var regions = new HashSet <int>(connectorRegions[pos].Select(region => merged[region])); if (regions.Count > 1) { return(false); } // This connecter isn't needed, but connect it occasionally so that the // dungeon isn't singly-connected. if (Randomizer.ChanceIn(extraConnectorChance)) { AddJunction(pos); } return(true); }); } }