Beispiel #1
0
        /// <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;
     }
 }
Beispiel #3
0
        /// <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;
                    }
                }
            }
        }
Beispiel #6
0
        /// <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;
            }
        }
Beispiel #7
0
 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;
     }
 }
Beispiel #8
0
        /// <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);
        }
Beispiel #9
0
        /// <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));
            }
        }
Beispiel #11
0
 private static void CarveRoom(ISettableMapView <bool> map, Rectangle room)
 {
     foreach (var position in map.Positions())
     {
         map[position] = true;
     }
 }
Beispiel #12
0
 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;
         }
     }
 }
Beispiel #14
0
 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;
         }
     }
 }
Beispiel #15
0
        /// <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];
                }
            }
        }
Beispiel #18
0
        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;
                    }
                }
            }
        }
Beispiel #19
0
        /// <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];
                }
            }
        }
Beispiel #20
0
        /// <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);
        }
Beispiel #21
0
 /// <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;
             }
         }
     }
 }
Beispiel #22
0
        /// <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);
        }
Beispiel #23
0
        /// <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);
            }
        }
Beispiel #24
0
        /// <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);
            }
        }
Beispiel #25
0
        /// <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);
            }
        }
Beispiel #26
0
        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;
             }
         }
     }
 }
Beispiel #29
0
        /// <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)
 {
 }