/// <summary> /// Effectively a helper-constructor. Constructs a map using an <see cref="ISettableMapView{T}"/> for the terrain map, where type T can /// be any type that implements <see cref="IGameObject"/>. Note that a Map that is constructed using this function will throw an /// <see cref="InvalidCastException"/> if any IGameObject is given to <see cref="SetTerrain(IGameObject)"/> that cannot be casted to type T. /// </summary> /// <remarks> /// Suppose you have a class MyTerrain that inherits from BaseClass and implements <see cref="IGameObject"/>. This construction function allows /// you to construct your map using an <see cref="ISettableMapView{MyTerrain}"/> instance as the terrain map, which you cannot do with the regular /// constructor since <see cref="ISettableMapView{MyTerrain}"/> does not satisfy the constructor's type requirement of /// <see cref="ISettableMapView{IGameObject}"/>. /// /// Since this function under the hood creates a <see cref="SettableTranslationMap{T, IGameObject}"/> that translates to/from IGameObject as needed, /// any change made using the map's <see cref="SetTerrain(IGameObject)"/> function will be reflected both in the map and in the original /// ISettableMapView. /// </remarks> /// <typeparam name="T">The type of terrain that will be stored in the created Map. Can be any type that implements <see cref="IGameObject"/>.</typeparam> /// <param name="terrainLayer">The <see cref="ISettableMapView{T}"/> that represents the terrain layer for this map. After the /// map has been created, you should use the <see cref="SetTerrain(IGameObject)"/> function to modify the values in this map view, rather /// than setting the values via the map view itself -- if you re-assign the value at a location via the map view, the /// <see cref="ObjectAdded"/>/<see cref="ObjectRemoved"/> events are NOT guaranteed to be called, and many invariants of map may not be properly /// enforced.</param> /// <param name="numberOfEntityLayers">Number of non-terrain layers for the map.</param> /// <param name="distanceMeasurement"><see cref="Distance"/> measurement to use for pathing/measuring distance on the map.</param> /// <param name="layersBlockingWalkability">Layer mask containing those layers that should be allowed to have items that block walkability. /// Defaults to all layers.</param> /// <param name="layersBlockingTransparency">Layer mask containing those layers that should be allowed to have items that block FOV. /// Defaults to all layers.</param> /// <param name="entityLayersSupportingMultipleItems">Layer mask containing those layers that should be allowed to have multiple objects at the same /// location on the same layer. Defaults to no layers.</param> /// <returns>A new Map whose terrain is created using the given terrainLayer, and with the given parameters.</returns> public static Map CreateMap <T>(ISettableMapView <T> terrainLayer, int numberOfEntityLayers, Distance distanceMeasurement, uint layersBlockingWalkability = uint.MaxValue, uint layersBlockingTransparency = uint.MaxValue, uint entityLayersSupportingMultipleItems = 0) where T : IGameObject { var terrainMap = new LambdaSettableTranslationMap <T, IGameObject>(terrainLayer, t => t, g => (T)g); return(new Map(terrainMap, numberOfEntityLayers, distanceMeasurement, layersBlockingWalkability, layersBlockingTransparency, entityLayersSupportingMultipleItems)); }
static private void createRoom(ISettableMapView <bool> map, Rectangle room) { foreach (var pos in room.Positions()) { map[pos] = true; } }
/// <summary> /// Connects the map areas given on the given map using the algorithm described in the class description. /// </summary> /// <param name="map">The map to connect.</param> /// <param name="mapAreas">The map areas to connect on the given map.</param> /// <param name="distanceCalc">The distance calculation that defines distance/neighbors.</param> /// <param name="areaConnector"> /// The area connection strategy to use. Not all methods function on maps with concave areas /// -- see respective class documentation for details. /// </param> /// /// /// <param name="tunnelCreator"> /// The tunnel creation strategy to use. If null is specified, DirectLineTunnelCreator with /// the distance calculation specified is used. /// </param> static public void Connect(ISettableMapView <bool> map, IReadOnlyList <MapArea> mapAreas, Distance distanceCalc, IAreaConnectionPointSelector areaConnector = null, ITunnelCreator tunnelCreator = null) { if (areaConnector == null) { areaConnector = new RandomConnectionPointSelector(); } if (tunnelCreator == null) { tunnelCreator = new DirectLineTunnelCreator(distanceCalc); } var ds = new DisjointSet(mapAreas.Count); while (ds.Count > 1) // Haven't unioned all sets into one { for (int i = 0; i < mapAreas.Count; i++) { int iClosest = findNearestMapArea(mapAreas, distanceCalc, i, ds); var connectionPoints = areaConnector.SelectConnectionPoints(mapAreas[i], mapAreas[iClosest]); tunnelCreator.CreateTunnel(map, connectionPoints.Item1, connectionPoints.Item2); ds.MakeUnion(i, iClosest); } } }
static private int countWallsNear(ISettableMapView <bool> mapToUse, int posX, int posY, int distance) { int count = 0; int xMin = Math.Max(posX - distance, 0); int xMax = Math.Min(posX + distance, mapToUse.Width - 1); int yMin = Math.Max(posY - distance, 0); int yMax = Math.Min(posY + distance, mapToUse.Height - 1); for (int x = xMin; x <= xMax; x++) { for (int y = yMin; y <= yMax; y++) { if (x == posX && y == posY) { continue; } if (!mapToUse[x, y]) { ++count; } } } return(count); }
static private void cellAutoBigAreaAlgo(ISettableMapView <bool> map) { var oldMap = new ArrayMap <bool>(map.Width, map.Height); for (int x = 0; x < map.Width; x++) { for (int y = 0; y < map.Height; y++) { oldMap[x, y] = map[x, y]; } } for (int x = 0; x < map.Width; x++) { for (int y = 0; y < map.Height; y++) { if (x == 0 || y == 0 || x == map.Width - 1 || y == map.Height - 1) { continue; } if (countWallsNear(oldMap, x, y, 1) >= 5 || countWallsNear(oldMap, x, y, 2) <= 2) { map[x, y] = false; } else { map[x, y] = true; } } } }
/// <summary> /// Implements the algorithm, creating the tunnel as specified in the class description. /// </summary> /// <param name="map">The map to create the tunnel on.</param> /// <param name="start">Start coordinate of the tunnel.</param> /// <param name="end">End coordinate of the tunnel.</param> public void CreateTunnel(ISettableMapView <bool> map, Coord start, Coord end) { var lineAlgorithm = (adjacencyRule == AdjacencyRule.CARDINALS) ? Lines.Algorithm.ORTHO : Lines.Algorithm.BRESENHAM; Coord previous = Coord.NONE; foreach (var pos in Lines.Get(start, end, lineAlgorithm)) { map[pos] = true; // Previous cell, and we're going vertical, go 2 wide so it looks nicer Make sure not // to break rectangles (less than last index)! if (previous != Coord.NONE) // TODO: Make double wide vert an option { if (pos.Y != previous.Y) { if (pos.X + 1 < map.Width - 1) { map[pos.X + 1, pos.Y] = true; } } } previous = pos; } }
static private void createVTunnel(ISettableMapView <bool> map, int yStart, int yEnd, int xPos) { for (int y = Math.Min(yStart, yEnd); y <= Math.Max(yStart, yEnd); ++y) { map[xPos, y] = true; } }
/// <summary> /// Generates a cave-like map using the cellular automata algorithm here: /// http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels. /// See <see cref="Generators.CellularAutomataAreaGenerator"/> for details. This algorithm is identical, except that /// it connects the areas automatically afterward. /// </summary> /// <param name="map"></param> /// <param name="rng"></param> /// <param name="fillProbability"></param> /// <param name="totalIterations"></param> /// <param name="cutoffBigAreaFill"></param> /// <returns>Collection of areas representing the areas of the map before they were connected.</returns> public static IEnumerable <MapArea> GenerateCellularAutomataMap(ISettableMapView <bool> map, IGenerator rng = null, int fillProbability = 40, int totalIterations = 7, int cutoffBigAreaFill = 4) { if (rng == null) { rng = SingletonRandom.DefaultRNG; } // Optimization to allow us to use the existing map if the existing map is an ArrayMap. // otherwise we allocate a temporary ArrayMap and copy it onto the original at the end, // to make sure that we set each value in the final array map only once in the case that // it is something less "quick" than an ArrayMap. (ArrayMap <bool> tempMap, bool wasArrayMap) = getStartingArray(map); // TODO: The above optimization causes an extra arraymap copy in the case of CellularAutomata -- it may be useful to // attempt to eliminate this copy for performance reasons // Generate map Generators.CellularAutomataAreaGenerator.Generate(tempMap, rng, fillProbability, totalIterations, cutoffBigAreaFill); // Calculate connected areas and store before we connect different rooms var areas = MapAreaFinder.MapAreasFor(tempMap, AdjacencyRule.CARDINALS).ToList(); // Connect randomly Connectors.ClosestMapAreaConnector.Connect(tempMap, Distance.MANHATTAN, new Connectors.RandomConnectionPointSelector(rng)); if (!wasArrayMap) { map.ApplyOverlay(tempMap); } return(areas); }
/// <summary> /// Connects the areas by determining all unique areas on the map given using a <see cref="MapAreaFinder"/>, /// and then, if <paramref name="randomizeOrder"/> is true, performing a Fisher Yates shuffle of that /// list of areas found. It then simply connects areas adjacent to each other in that list, /// using the methods specified to determine points within two areas to connect, and how to /// create the tunnel between the two points. /// </summary> /// <param name="map">The map to connect.</param> /// <param name="adjacencyRule"> /// Method of adjacency to respect when determining map areas. Cannot be diagonal. /// </param> /// <param name="areaConnector"> /// The method to use to determine the points from two areas to make a tunnel between, in /// order to connect those two areas. If null is specified, a <see cref="RandomConnectionPointSelector"/> /// is used, that uses the RNG passed into this function. /// </param> /// <param name="tunnelCreator"> /// The tunnel creation strategy to use. If null is specified, /// <see cref="HorizontalVerticalTunnelCreator"/> that utilizes the RNG passed into this function is used. /// </param> /// <param name="rng">The rng to use. If null is specified, the default rng is assumed.</param> /// <param name="randomizeOrder"> /// Whether or not to randomize which room is connected to which -- if this is set to false, /// they will be conencted in the exact order they are returned from the <see cref="MapAreaFinder"/>. /// </param> static public void Connect(ISettableMapView <bool> map, AdjacencyRule adjacencyRule, IAreaConnectionPointSelector areaConnector = null, ITunnelCreator tunnelCreator = null, IGenerator rng = null, bool randomizeOrder = true) { if (adjacencyRule == AdjacencyRule.DIAGONALS) { throw new System.ArgumentException("Cannot specify diagonal adjacency for map connections.", nameof(adjacencyRule)); } if (rng == null) { rng = SingletonRandom.DefaultRNG; } if (areaConnector == null) { areaConnector = new RandomConnectionPointSelector(rng); } if (tunnelCreator == null) { tunnelCreator = new HorizontalVerticalTunnelCreator(rng); } var areas = MapAreaFinder.MapAreasFor(map, adjacencyRule).ToList(); if (randomizeOrder) { areas.FisherYatesShuffle(rng); } Connect(map, areas, areaConnector, tunnelCreator); }
/// <summary> /// Generates the map. Floor tiles will be set to true in the provided map, and wall tiles /// will be set to false. /// </summary> /// <param name="map">The map to fill with values when generate is called.</param> /// <param name="rng"> /// The RNG to use to initially fill the map. If null is specified, the default RNG is used. /// </param> /// <param name="fillProbability"> /// Represents the percent chance that a given cell will be a floor cell when the map is /// initially randomly filled. Recommended to be in range [40, 60] (40 is used in the /// roguebasin article). /// </param> /// <param name="totalIterations"> /// Total number of times the cellular automata-based smoothing algorithm is executed. /// Recommended to be in range [2, 10] (7 is used on roguebasin article). /// </param> /// <param name="cutoffBigAreaFill"> /// Total number of times the cellular automata smoothing variation that is more likely to /// result in "breaking up" large areas will be run before switching to the more standard /// nearest neighbors version. Recommended to be in range [2, 7] (4 is used in roguebasin article). /// </param> /// <param name="connectUsingDefault"> /// Whether or not to ensure all areas generated are connected. If this is true, /// ClosestMapAreaConnector.Connect will be used to connect the areas, with /// Distance.MANHATTAN distance used, the RNG given, a RandomConnectionPointSelector that /// uses the RNG specified to this function, and default values for all other optional /// parameters of ClosestMapAreaConnector.Connect. /// </param> static public void Generate(ISettableMapView <bool> map, IRandom rng = null, int fillProbability = 40, int totalIterations = 7, int cutoffBigAreaFill = 4, bool connectUsingDefault = true) { if (rng == null) { rng = SingletonRandom.DefaultRNG; } randomlyFillCells(map, rng, fillProbability); for (int i = 0; i < totalIterations; i++) { if (i < cutoffBigAreaFill) { cellAutoBigAreaAlgo(map); } else { cellAutoNearestNeighborsAlgo(map); } } // Ensure it's enclosed before we try to connect, so we can't possibly connect a path // that ruins the enclosure. Doing this before connection ensures that filling it can't // kill the path to an area. fillToRectangle(map); if (connectUsingDefault) { Connectors.ClosestMapAreaConnector.Connect(map, Distance.MANHATTAN, new Connectors.RandomConnectionPointSelector(rng)); } }
private static void CarveRoom(ISettableMapView <bool> map, Rectangle room) { foreach (var position in map.Positions()) { map[position] = true; } }
static private void createHTunnel(ISettableMapView <bool> map, int xStart, int xEnd, int yPos) { for (int x = Math.Min(xStart, xEnd); x <= Math.Max(xStart, xEnd); ++x) { map[x, yPos] = true; } }
static private void createRoom(ISettableMapView <bool> map, Rectangle room) { for (int x = room.X + 1; x < room.MaxExtentX; x++) { for (int y = room.Y + 1; y < room.MaxExtentY; y++) { map[x, y] = true; } } }
private void TestMap(ISettableMapView <bool> wMap) { // Rectangle for (var x = 1; x < wMap.Width - 1; x++) { for (var y = 1; y < wMap.Height - 1; y++) { wMap[x, y] = true; } } }
/// <summary> /// Constructor. Takes an existing map view to create a view from and applies view data to it. /// </summary> /// <param name="baseMap">Your underlying map data.</param> /// <param name="overlay">The view data to apply to the map. Must have identical dimensions /// to baseMap.</param> /// <param name="getter">The TranslateGet implementation.</param> /// <param name="setter">The TranslateSet implementation.</param> public LambdaTranslationMap(ISettableMapView <T1> baseMap, ISettableMapView <T2> overlay, Func <T1, T2> getter, Func <T2, T1> setter) : base(baseMap) { _getter = getter ?? throw new ArgumentNullException(nameof(getter)); _setter = setter ?? throw new ArgumentNullException(nameof(setter)); ApplyOverlay(overlay); }
static private void fillToRectangle(ISettableMapView <bool> map) { for (int x = 0; x < map.Width; x++) { map[x, 0] = false; map[x, map.Height - 1] = false; } for (int y = 0; y < map.Height; y++) { map[0, y] = false; map[map.Width - 1, y] = false; } }
/// <summary> /// Extension method that applies values of the overlay to the current one -- effectively /// sets all the values of the current map to be corresponding to the one you pass in. /// </summary> /// <param name="self"> /// The current ISettableMapView. Never specified manually as this is an extension method. /// </param> /// <param name="overlay"> /// The data apply to the map. Must have identical dimensions to the current map. /// </param> public static void ApplyOverlay <T>(this ISettableMapView <T> self, IMapView <T> overlay) { if (self.Height != overlay.Height || self.Width != overlay.Width) { throw new ArgumentException("Overlay size must match current map size."); } for (int y = 0; y < self.Height; ++y) { for (int x = 0; x < self.Width; ++x) { self[x, y] = overlay[x, y]; } } }
public static void ReadMap(string filePath, ISettableMapView <bool> map, char wallChar = '#') { using (var reader = new StreamReader(filePath)) { for (int row = 0; row < map.Height; row++) { string line = reader.ReadLine(); for (int col = 0; col < map.Width; col++) { map[col, row] = (line[col] == wallChar) ? false : true; } } } }
/// <summary> /// Applies view data to your map. /// </summary> /// <param name="overlay">The view data to apply to the map. Must have identical dimensions /// to BaseMap.</param> public void ApplyOverlay(ISettableMapView <T2> overlay) { if (_baseMap.Height != overlay.Height || _baseMap.Width != overlay.Width) { throw new ArgumentException("Overlay size must match base map size."); } for (int y = 0; y < _baseMap.Height; ++y) { for (int x = 0; x < _baseMap.Width; ++x) { this[x, y] = overlay[x, y]; } } }
/// <summary> /// Constructor. Takes an existing map view to create a view from, and getter/setter /// functions taking only a map value. /// </summary> /// <remarks> /// If a position is also needed to perform the translation, an overload is provided taking /// corresponding functions. /// </remarks> /// <param name="baseMap">Your underlying map data.</param> /// <param name="getter">The TranslateGet implementation.</param> /// <param name="setter">The TranslateSet implementation.</param> public LambdaSettableTranslationMap(ISettableMapView <T1> baseMap, Func <T1, T2> getter, Func <T2, T1> setter) : base(baseMap) { if (getter == null) { throw new ArgumentNullException(nameof(getter)); } if (setter == null) { throw new ArgumentNullException(nameof(setter)); } _getter = (pos, t1) => getter(t1); _setter = (pos, t2) => setter(t2); }
/// <summary> /// Generates the map, setting the map given as a "walkability map". Wall tiles (the edges of /// the map) will have a value of false set in the given map, whereas true will be set to all /// non-wall tiles. It is guaranteed that the "set" function of the ISettableMapView passed /// in will only be called once per location. /// </summary> /// /// /// <param name="map">The map to set values to.</param> static public void Generate(ISettableMapView <bool> map) { for (int x = 0; x < map.Width; x++) { for (int y = 0; y < map.Height; y++) { if (x == 0 || y == 0 || x == map.Width - 1 || y == map.Height - 1) { map[x, y] = false; } else { map[x, y] = true; } } } }
/// <summary> /// Generates a cave-like map using the cellular automata algorithm here: /// http://www.roguebasin.com/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels. /// See CellularAutomataAreaGenerator for details. This algorithm is identical, except that /// it connects the areas automatically afterward. /// </summary> /// <param name="map"></param> /// <param name="rng"></param> /// <param name="fillProbability"></param> /// <param name="totalIterations"></param> /// <param name="cutoffBigAreaFill"></param> /// <returns></returns> public static IEnumerable <MapArea> GenerateCellularAutomataMap(ISettableMapView <bool> map, IGenerator rng = null, int fillProbability = 40, int totalIterations = 7, int cutoffBigAreaFill = 4) { if (rng == null) { rng = SingletonRandom.DefaultRNG; } // Generate map Generators.CellularAutomataAreaGenerator.Generate(map, rng, fillProbability, totalIterations, cutoffBigAreaFill); // Calculate connected areas and store before we connect different rooms var areas = MapAreaFinder.MapAreasFor(map, AdjacencyRule.CARDINALS).ToList(); // Connect randomly Connectors.ClosestMapAreaConnector.Connect(map, Distance.MANHATTAN, new Connectors.RandomConnectionPointSelector(rng)); return(areas); }
/// <summary> /// Connects the areas by simply connecting areas adjacent to each other in the passed in /// list of areas, using the methods specified to determine points within two areas to /// connect, and how to create the tunnel between the two points. /// </summary> /// <param name="map">The map to connect.</param> /// <param name="mapAreas"> /// The list of map areas to connect, in the order they should be connected. /// </param> /// <param name="areaConnector"> /// The method to use to determine the points from two areas to make a tunnel between, in /// order to connect those two areas. If null is specified, a <see cref="RandomConnectionPointSelector"/> /// is used, that uses the default RNG. /// </param> /// <param name="tunnelCreator"> /// The tunnel creation strategy to use. If null is specified, a <see cref="HorizontalVerticalTunnelCreator"/> /// that utilizes the default RNG is used. /// </param> static public void Connect(ISettableMapView <bool> map, IReadOnlyList <IReadOnlyMapArea> mapAreas, IAreaConnectionPointSelector areaConnector = null, ITunnelCreator tunnelCreator = null) { if (areaConnector == null) { areaConnector = new RandomConnectionPointSelector(); } if (tunnelCreator == null) { tunnelCreator = new HorizontalVerticalTunnelCreator(); } for (int i = 1; i < mapAreas.Count; i++) { var connectionPoints = areaConnector.SelectConnectionPoints(mapAreas[i - 1], mapAreas[i]); tunnelCreator.CreateTunnel(map, connectionPoints.Item1, connectionPoints.Item2); } }
/// <summary> /// Implemnets the algorithm, creating the tunnel as specified in the class description. /// </summary> /// <param name="map">The map to create the tunnel on.</param> /// <param name="start">Start coordinate of the tunnel.</param> /// <param name="end">End coordinate of the tunnel.</param> public void CreateTunnel(ISettableMapView <bool> map, Coord start, Coord end) { if (rng == null) { rng = SingletonRandom.DefaultRNG; } if (rng.NextBoolean()) { createHTunnel(map, start.X, end.X, start.Y); createVTunnel(map, start.Y, end.Y, end.X); } else { createVTunnel(map, start.Y, end.Y, start.X); createHTunnel(map, start.X, end.X, end.Y); } }
/// <summary> /// Implemnets the algorithm, creating the tunnel as specified in the class description. /// </summary> /// <param name="map">The map to create the tunnel on.</param> /// <param name="start">Start coordinate of the tunnel.</param> /// <param name="end">End coordinate of the tunnel.</param> public void CreateTunnel(ISettableMapView <bool> map, Coord start, Coord end) { if (rng == null) { rng = SingletonRandom.DefaultRNG; } if (rng.Next(2) == 0) // Favors vertical tunnels { createHTunnel(map, start.X, end.X, start.Y); createVTunnel(map, start.Y, end.Y, end.X); } else { createVTunnel(map, start.Y, end.Y, start.X); createHTunnel(map, start.X, end.X, end.Y); } }
public static void GenerateBSPDungeon(ISettableMapView <bool> map) { _leaves = new List <Leaf>(); var random = SingletonRandom.DefaultRNG; // start with a root leaf the size of the entire dungeon to start the process var rootLeaf = new Leaf(0, 0, map.Width, map.Height); _leaves.Add(rootLeaf); // loop until no longer able to split var didSplit = true; while (didSplit) { didSplit = false; // TODO: BUG: Unhandled exception. System.InvalidOperationException: Collection was modified; enumeration operation may not execute. foreach (var leaf in _leaves) { if (leaf.LeftChild == null && leaf.RightChild == null) { if (leaf.Width > MaxLeafSize || leaf.Height > MaxLeafSize || random.NextDouble() > 0.25f) { if (leaf.Split()) { _leaves.Add(leaf.LeftChild); _leaves.Add(leaf.RightChild); didSplit = true; } } } } } rootLeaf.CreateRooms(); // carve out the rooms foreach (var leaf in _leaves) { CarveRoom(map, leaf.Room); } }
/// <summary> /// Generates the areas. Floor tiles will be set to true in the provided map, and wall tiles /// will be set to false. /// </summary> /// <param name="map">The map to fill with values when generate is called.</param> /// <param name="rng"> /// The RNG to use to initially fill the map. If null is specified, the default RNG is used. /// </param> /// <param name="fillProbability"> /// Represents the percent chance that a given cell will be a floor cell when the map is /// initially randomly filled. Recommended to be in range [40, 60] (40 is used in the /// roguebasin article). /// </param> /// <param name="totalIterations"> /// Total number of times the cellular automata-based smoothing algorithm is executed. /// Recommended to be in range [2, 10] (7 is used on roguebasin article). /// </param> /// <param name="cutoffBigAreaFill"> /// Total number of times the cellular automata smoothing variation that is more likely to /// result in "breaking up" large areas will be run before switching to the more standard /// nearest neighbors version. Recommended to be in range [2, 7] (4 is used in roguebasin article). /// </param> static public void Generate(ISettableMapView <bool> map, IGenerator rng = null, int fillProbability = 40, int totalIterations = 7, int cutoffBigAreaFill = 4) { if (rng == null) { rng = SingletonRandom.DefaultRNG; } // We must allocate a new one to avoid messing up other map gen features that happened on // the original var tempMap = new ArrayMap <bool>(map.Width, map.Height); tempMap.ApplyOverlay(map); // Sets each cell so as of this point, tempMap is in a clean state randomlyFillCells(tempMap, rng, fillProbability); for (int i = 0; i < totalIterations; i++) { if (i < cutoffBigAreaFill) { cellAutoBigAreaAlgo(tempMap); } else { cellAutoNearestNeighborsAlgo(tempMap); } } // Ensure it's enclosed before we try to connect, so we can't possibly connect a path // that ruins the enclosure. Doing this before connection ensures that filling it can't // kill the path to an area. fillToRectangle(tempMap); // Set rooms to true, but do NOT enforce where walls (false values) are -- this is done // by making sure the blank slate passed in is all false. foreach (var pos in tempMap.Positions()) { if (tempMap[pos]) { map[pos] = true; } } }
static private void randomlyFillCells(ISettableMapView <bool> map, IGenerator rng, int fillProbability) { for (int x = 0; x < map.Width; x++) { for (int y = 0; y < map.Height; y++) { if (x == 0 || y == 0 || x == map.Width - 1 || y == map.Height - 1) // Borders are always walls { map[x, y] = false; } else if (rng.Next(100) < fillProbability) { map[x, y] = true; } else { map[x, y] = false; } } } }
/// <summary> /// Constructor. Constructs map with the given terrain layer, determining width/height based on the width/height of that terrain layer. /// </summary> /// <remarks> /// Because of the way polymorphism works for custom classes in C#, the <paramref name="terrainLayer"/> parameter MUST be of type /// <see cref="ISettableMapView{IGameObject}"/>, rather than <see cref="ISettableMapView{T}"/> where T is a type that derives from or implements /// <see cref="IGameObject"/>. If you need to use a map view storing type T rather than IGameObject, use the /// <see cref="CreateMap{T}(ISettableMapView{T}, int, Distance, uint, uint, uint)"/> function to create the map. /// </remarks> /// <param name="terrainLayer">The <see cref="ISettableMapView{IGameObject}"/> that represents the terrain layer for this map. After the /// map has been created, you should use the <see cref="SetTerrain(IGameObject)"/> function to modify the values in this map view, rather /// than setting the values via the map view itself -- if you re-assign the value at a location via the map view, the /// <see cref="ObjectAdded"/>/<see cref="ObjectRemoved"/> events are NOT guaranteed to be called, and many invariants of map may not be properly /// enforced.</param> /// <param name="numberOfEntityLayers">Number of non-terrain layers for the map.</param> /// <param name="distanceMeasurement"><see cref="Distance"/> measurement to use for pathing/measuring distance on the map.</param> /// <param name="layersBlockingWalkability">Layer mask containing those layers that should be allowed to have items that block walkability. /// Defaults to all layers.</param> /// <param name="layersBlockingTransparency">Layer mask containing those layers that should be allowed to have items that block FOV. /// Defaults to all layers.</param> /// <param name="entityLayersSupportingMultipleItems">Layer mask containing those layers that should be allowed to have multiple objects at the same /// location on the same layer. Defaults to no layers.</param> public Map(ISettableMapView <IGameObject> terrainLayer, int numberOfEntityLayers, Distance distanceMeasurement, uint layersBlockingWalkability = uint.MaxValue, uint layersBlockingTransparency = uint.MaxValue, uint entityLayersSupportingMultipleItems = 0) { _terrain = terrainLayer; Explored = new ArrayMap <bool>(_terrain.Width, _terrain.Height); _entities = new LayeredSpatialMap <IGameObject>(numberOfEntityLayers, 1, entityLayersSupportingMultipleItems); LayersBlockingWalkability = layersBlockingWalkability; LayersBlockingTransparency = layersBlockingTransparency; _entities.ItemAdded += (s, e) => ObjectAdded?.Invoke(this, e); _entities.ItemRemoved += (s, e) => ObjectRemoved?.Invoke(this, e); _entities.ItemMoved += (s, e) => ObjectMoved?.Invoke(this, e); if (layersBlockingTransparency == 1) // Only terrain so we optimize { TransparencyView = new LambdaMapView <bool>(_terrain.Width, _terrain.Height, c => _terrain[c] == null || _terrain[c].IsTransparent); } else { TransparencyView = new LambdaMapView <bool>(_terrain.Width, _terrain.Height, FullIsTransparent); } if (layersBlockingWalkability == 1) // Similar, only terrain blocks, so optimize { WalkabilityView = new LambdaMapView <bool>(_terrain.Width, _terrain.Height, c => _terrain[c] == null || _terrain[c].IsWalkable); } else { WalkabilityView = new LambdaMapView <bool>(_terrain.Width, _terrain.Height, FullIsWalkable); } _fov = new FOV(TransparencyView); AStar = new AStar(WalkabilityView, distanceMeasurement); }
/// <summary> /// Constructor. Takes the MapView to represent. Initial ViewArea will be the entire MapView. /// </summary> /// <param name="mapView">The MapView to represent.</param> public SettableViewport(ISettableMapView <T> mapView) : base(mapView) { }