/// <summary> /// Fills a region with a given tile. /// </summary> /// <param name="map">WorldMap to use</param> /// <param name="X">X coordinate of starting point</param> /// <param name="Y">Y coordinate of starting point</param> /// <param name="From">Tile to replace</param> /// <param name="To">Tile to replace with</param> /// <returns>Amount of tiles filled</returns> static List <Point> FloodFill(WorldMap map, int X, int Y, WorldMap.TileType From, WorldMap.TileType To) { List <Point> points = new List <Point>(); if (X < 0 || Y < 0 || X >= map.Width || Y >= map.Height) { return(points); } if (map.TileData[X, Y] != From) { return(points); } map.TileData[X, Y] = To; points.Add(new Point(X, Y)); Queue <Point> Q = new Queue <Point>(); Q.Enqueue(new Point(X, Y)); while (Q.Count() > 0) { Point n = Q.Dequeue(); X = n.X; Y = n.Y; if (X + 1 < map.Width && Y < map.Height && map.TileData[X + 1, Y] == From) { map.TileData[X + 1, Y] = To; points.Add(new Point(X + 1, Y)); Q.Enqueue(new Point(X + 1, Y)); } if (X >= 0 && Y + 1 < map.Height && map.TileData[X, Y + 1] == From) { map.TileData[X, Y + 1] = To; points.Add(new Point(X, Y + 1)); Q.Enqueue(new Point(X, Y + 1)); } if (X - 1 >= 0 && Y >= 0 && map.TileData[X - 1, Y] == From) { map.TileData[X - 1, Y] = To; points.Add(new Point(X - 1, Y)); Q.Enqueue(new Point(X - 1, Y)); } if (X < map.Width && Y - 1 >= 0 && map.TileData[X, Y - 1] == From) { map.TileData[X, Y - 1] = To; points.Add(new Point(X, Y - 1)); Q.Enqueue(new Point(X, Y - 1)); } } return(points); /* * FloodFill(map, X + 1, Y + 1, From, To); * FloodFill(map, X + 1, Y - 1, From, To); * FloodFill(map, X - 1, Y - 1, From, To); * FloodFill(map, X - 1, Y + 1, From, To); * //*/ }
/// <summary> /// Simple search and replace. /// </summary> /// <param name="map">Working map</param> /// <param name="From">Tile to replace</param> /// <param name="To">Replacement tile</param> public static void Replace(WorldMap map, WorldMap.TileType From, WorldMap.TileType To) { for (int x = 0; x < map.Width; x++) { for (int y = 0; y < map.Height; y++) { if (map.TileData[x, y] == From) { map.TileData[x, y] = To; } } } }
/// <summary> /// Check for presence of a specific tile type in a specific direction (orthogonal) /// </summary> /// <param name="map">Working map</param> /// <param name="X">Starting point</param> /// <param name="Y">Starting point</param> /// <param name="Direction">Direction 0,1,2,3 - West is 0 and then clockwise</param> /// <param name="distance">Maximum distance to look</param> /// <param name="Tile"></param> /// <returns>distance or 9999 if not found</returns> public static int LookRayTile(WorldMap map, int X, int Y, int Direction, int distance, WorldMap.TileType Tile) { int D = 9999; int dX = 0; int dY = 0; if (Direction == DIR_E) { dX = 1; } if (Direction == DIR_W) { dX = -1; } if (Direction == DIR_N) { dY = -1; } if (Direction == DIR_S) { dY = 1; } for (int i = 0; i < distance; i++) { X += dX; Y += dY; if (X < 0 || X >= map.Width || Y < 0 || Y > map.Height) { return(D); } WorldMap.TileType Target = map.TileData[X, Y]; if (Target == Tile) { return(i); } } return(D); }
/// <summary> /// Places mountain tiles /// </summary> /// <param name="map">Working map</param> /// <param name="amount">Amount to place - currently unused</param> public static void DoMountains(WorldMap map, int amount) { int cutoff = 200; int cutoff2 = 160; int cutoff3 = 128; float MountainSizeDivisor = 1f / 32f; float MountainSpreadDivisor = 1f / 128f; float MountainFrequencyDivisor = 1f / 512f; float Noise1 = 1f / 4f; float Noise2 = 1f / 6f; for (int x = 1; x < map.Width - 1; x++) { for (int y = 1; y < map.Height - 1; y++) { int H = (int)Simplex.CalcPixel2D(x, y, MountainSizeDivisor); int M = (int)Simplex.CalcPixel2D(x, y, MountainFrequencyDivisor); int S = (int)Simplex.CalcPixel2D(x, y, MountainSpreadDivisor); int N1 = (int)Simplex.CalcPixel2D(x, y, Noise1); int N2 = (int)Simplex.CalcPixel2D(x, y, Noise2); if (H > cutoff && M > cutoff2 && S > cutoff3 && N1 > cutoff3 && N2 > cutoff3) { WorldMap.TileType Target = map.TileData[x, y]; if (Target != WorldMap.TileType.Ocean && Target != WorldMap.TileType.River) { map.TileData[x, y] = WorldMap.TileType.Mountain; //greatly raise actual mountain tiles map.ElevationData[x, y] = (float)Math.Pow((MathHelper.Clamp(map.ElevationData[x, y], 0f, 999f)), 1f / 10f); //raise tiles around them too if (map.TileData[x - 1, y] != WorldMap.TileType.Mountain) { map.ElevationData[x - 1, y] = (float)Math.Pow((MathHelper.Clamp(map.ElevationData[x - 1, y], 0f, 999f)), 1f / 3f); } if (map.TileData[x + 1, y] != WorldMap.TileType.Mountain) { map.ElevationData[x + 1, y] = (float)Math.Pow((MathHelper.Clamp(map.ElevationData[x + 1, y], 0f, 999f)), 1f / 3f); } if (map.TileData[x, y - 1] != WorldMap.TileType.Mountain) { map.ElevationData[x, y - 1] = (float)Math.Pow((MathHelper.Clamp(map.ElevationData[x, y - 1], 0f, 999f)), 1f / 3f); } if (map.TileData[x, y + 1] != WorldMap.TileType.Mountain) { map.ElevationData[x, y + 1] = (float)Math.Pow((MathHelper.Clamp(map.ElevationData[x, y + 1], 0f, 999f)), 1f / 3f); } } } } } /*/ * for (int i=0;i<amount;i++) * { * int X = RNG.NextInt(0, map.Width); * int Y = RNG.NextInt(0, map.Height); * WorldMap.TileType Target = map.TileData[X, Y]; * if (Target != WorldMap.TileType.Ocean && Target != WorldMap.TileType.River) * map.TileData[X, Y] = WorldMap.TileType.Mountain; * } * //*/ }
/// <summary> /// Populates a float 2D array with distance values on a given map from given tile type /// </summary> /// <param name="map">Working map</param> /// <param name="DistanceField">Initial 2D distance field</param> /// <param name="Target">Tile to calculate distances from</param> /// <returns>Computed 2D array distance field</returns> public static float[,] DoDistanceField(WorldMap map, float[,] DistanceField, WorldMap.TileType Target) { //pass 1: set all to something stupid like 9999 except target float MAX = 9999f; float Highest = 0f; float[] neighbourhood = new float[8]; for (int x = 0; x < map.Width; x++) { for (int y = 0; y < map.Height; y++) { if (map.TileData[x, y] == Target) { DistanceField[x, y] = 0; } else { DistanceField[x, y] = MAX; } } } //pass 2 & 3: pathfind for (int x = 1; x < map.Width - 1; x++) { for (int y = 1; y < map.Height - 1; y++) { //iterate over 8 neighbours, pick the smallest, add 1 neighbourhood[0] = DistanceField[x - 1, y - 1]; neighbourhood[1] = DistanceField[x, y - 1]; neighbourhood[2] = DistanceField[x + 1, y - 1]; neighbourhood[3] = DistanceField[x - 1, y]; neighbourhood[4] = DistanceField[x + 1, y]; neighbourhood[5] = DistanceField[x - 1, y + 1]; neighbourhood[6] = DistanceField[x, y + 1]; neighbourhood[7] = DistanceField[x + 1, y + 1]; float smallest = neighbourhood.Min(); //do not make any changes if no neighbours are lower and not the default value if (smallest < MAX && smallest < DistanceField[x, y]) { DistanceField[x, y] = smallest + 1; } } } //go from the other direction now for (int x = map.Width - 2; x > 0; x--) { for (int y = map.Height - 2; y > 0; y--) { //iterate over 8 neighbours, pick the smallest, add 1 neighbourhood[0] = DistanceField[x - 1, y - 1]; neighbourhood[1] = DistanceField[x, y - 1]; neighbourhood[2] = DistanceField[x + 1, y - 1]; neighbourhood[3] = DistanceField[x - 1, y]; neighbourhood[4] = DistanceField[x + 1, y]; neighbourhood[5] = DistanceField[x - 1, y + 1]; neighbourhood[6] = DistanceField[x, y + 1]; neighbourhood[7] = DistanceField[x + 1, y + 1]; float smallest = neighbourhood.Min(); //do not make any changes if no neighbours are lower and not the default value if (smallest < MAX && smallest < DistanceField[x, y]) { //add 1 to the distance - regardless if diagonal or straight DistanceField[x, y] = smallest + 1; //find the furthest value - use it for scaling down to highest being 1.0f and lowest being 0.0f if (Highest < smallest + 1) { Highest = smallest + 1; } } } } //pass 4: normalisation if (Highest != 0) { for (int x = 1; x < map.Width - 1; x++) { for (int y = 1; y < map.Height - 1; y++) { DistanceField[x, y] /= Highest; } } } return(DistanceField); }