public static void PopulateWithInitialData(TimelineLayer board, NamelessGame game) { var resolution = WorldGenConstants.Resolution; var random = new InternalRandom(game.WorldSettings.GlobalRandom.Next()); for (int x = 0; x < game.WorldSettings.WorldBoardWidth; x++) { for (int y = 0; y < game.WorldSettings.WorldBoardHeight; y++) { var worldTile = new WorldTile(new Microsoft.Xna.Framework.Point(x, y)); var tile = game.WorldSettings.TerrainGen.GetTileWithoutRiverWater(x, y, (float)game.WorldSettings.WorldBoardWidth / resolution); worldTile.Terrain = tile.Terrain; worldTile.Biome = tile.Biome; board.WorldTiles[x, y] = worldTile; } } //generate elevation map for river gen for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { //fill it with terrain heght with current noises using resolution board.ElevationMap[i][j] = game.WorldSettings.TerrainGen.GetHeightNoise(i, j, 1); } } //copy elevationArray var fillArray = new TileForGeneration[resolution][]; for (int i = 0; i < resolution; i++) { fillArray[i] = new TileForGeneration[resolution]; for (int j = 0; j < resolution; j++) { fillArray[i][j] = new TileForGeneration() { fillValue = board.ElevationMap[i][j], x = i, y = j, isWater = false }; } } List <FortuneSite> points = new List <FortuneSite>(); LinkedList <VEdge> edges = new LinkedList <VEdge>(); for (var i = 0; i < resolution; i++) { points.Add(new FortuneSite( random.Next(0, resolution - 1), random.Next(0, resolution - 1))); } //uniq the points points.Sort((p1, p2) => { if (p1.X.ApproxEqual(p2.X)) { if (p1.Y.ApproxEqual(p2.Y)) { return(0); } if (p1.Y < p2.Y) { return(-1); } return(1); } if (p1.X < p2.X) { return(-1); } return(1); }); var unique = new List <FortuneSite>(points.Count / 2); var last = points.First(); unique.Add(last); for (var index = 1; index < points.Count; index++) { var point = points[index]; if (!last.X.ApproxEqual(point.X) || !last.Y.ApproxEqual(point.Y)) { unique.Add(point); last = point; } } points = unique; edges = FortunesAlgorithm.Run(points, 0, 0, resolution - 1, resolution - 1); //VEdge.Start is a VPoint with location VEdge.Start.X and VEdge.End.Y //VEdge.End is the ending point for the edge //FortuneSite.Neighbors contains the site's neighbors in the Delaunay Triangulation var waterBitmap = new Bitmap(resolution, resolution); var graphics = Graphics.FromImage(waterBitmap); Pen whitePen = new Pen(System.Drawing.Color.White, 1); var edgesByTheSea = edges.Where( x => board.ElevationMap[(int)x.Start.X][(int)x.Start.Y] < TileNoiseInterpreter.SeaLevelThreshold && board.ElevationMap[(int)x.End.X][(int)x.End.Y] >= TileNoiseInterpreter.SeaLevelThreshold || board.ElevationMap[(int)x.End.X][(int)x.End.Y] < TileNoiseInterpreter.SeaLevelThreshold && board.ElevationMap[(int)x.Start.X][(int)x.Start.Y] >= TileNoiseInterpreter.SeaLevelThreshold ); var allInlandEdges = edges.Where( x => board.ElevationMap[(int)x.End.X][(int)x.End.Y] >= TileNoiseInterpreter.SeaLevelThreshold && board.ElevationMap[(int)x.Start.X][(int)x.Start.Y] >= TileNoiseInterpreter.SeaLevelThreshold ).ToList(); //randommly remove some rivers; var cullingChance = 15; var culledEdges = allInlandEdges.Where(edge => random.Next(1, 101) > cullingChance).ToList(); culledEdges.AddRange(edgesByTheSea.Where(edge => random.Next(1, 101) > cullingChance)); //remove desert rivers with high probability var cullingDesertChance = 90; //if not in desert biome ignore, if in desert, cull with high probability culledEdges = culledEdges.Where(edge => (board.WorldTiles[(int)edge.Start.X, (int)edge.Start.Y].Biome.HasFlag(Biomes.Desert | Biomes.Jungle | Biomes.Savannah) && board.WorldTiles[(int)edge.End.X, (int)edge.End.Y].Biome.HasFlag(Biomes.Desert | Biomes.Jungle | Biomes.Savannah)) || random.Next(1, 101) < cullingDesertChance).ToList(); var finalEdges = new List <VEdge>(); finalEdges.AddRange(culledEdges); foreach (var edge in finalEdges) { var pointCountForLerpAndRandomization = 7; var listVectorEdgePoints = new List <Vector2>(); var startVector = new Vector2((float)edge.Start.X, (float)edge.Start.Y); var endVector = new Vector2((float)edge.End.X, (float)edge.End.Y); var perpendicular = (endVector - startVector); perpendicular.Normalize(); perpendicular = new Vector2(perpendicular.Y, -perpendicular.X); listVectorEdgePoints.Add(startVector); var riverWiggle = 3; for (int i = 1; i < pointCountForLerpAndRandomization - 1; i++) { var newPoint = Vector2.Lerp(startVector, endVector, ((float)i / (pointCountForLerpAndRandomization - 1))) + (perpendicular * (random.Next(-riverWiggle, riverWiggle))); listVectorEdgePoints.Add(newPoint); } listVectorEdgePoints.Add(endVector); graphics.DrawCurve(whitePen, listVectorEdgePoints.Select(x => x.ToPoint().ToPoint()).ToArray()); } for (int x = 3; x < resolution - 3; x++) { for (int y = 3; y < resolution - 3; y++) { if (waterBitmap.GetPixel(x, y).R > 0) { //fillArray[x][y].isWater = true; if (board.ElevationMap[x][y] >= TileNoiseInterpreter.SeaLevelThreshold) { fillArray[x][y].isWater = true; } else { Queue <TileForGeneration> neighbours = new Queue <TileForGeneration>(); GenerationUtility.GetNeighbours(fillArray, neighbours, x, y, 2); if (neighbours.Any(n => board.ElevationMap[n.x][n.y] >= TileNoiseInterpreter.SeaLevelThreshold)) { fillArray[x][y].isWater = true; } } } } } for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { board.RiverMap[i][j] = fillArray[i][j].isWater; } } var riverBorderMapCopyForCalcultaion = new bool[resolution][]; for (int i = 0; i < resolution; i++) { riverBorderMapCopyForCalcultaion[i] = new bool[resolution]; for (int j = 0; j < resolution; j++) { if (board.RiverMap[i][j]) { bool borderedByAnythingBesidesWater = false; //not really a radius, more like an side lenght of a square var searchRadius = 3; if (i > searchRadius && i < resolution - searchRadius && j > searchRadius && j < resolution - searchRadius) { for (int k = i - searchRadius; k < i + searchRadius + 1; k++) { for (int l = j - searchRadius; l < j + searchRadius + 1; l++) { if (!board.RiverMap[k][l]) { borderedByAnythingBesidesWater = true; } } } } if (borderedByAnythingBesidesWater) { board.RiverBorderMap[i][j] = true; riverBorderMapCopyForCalcultaion[i][j] = true; } } } } var pointsNotConnectedToStartingPoints = new List <TileForGeneration>(); for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { if (board.RiverBorderMap[i][j]) { pointsNotConnectedToStartingPoints.Add(fillArray[i][j]); } } } GenerationUtility.AnalyzeAndAddLines(pointsNotConnectedToStartingPoints, riverBorderMapCopyForCalcultaion, out var borderLines); for (int i = 0; i < resolution; i++) { for (int j = 0; j < resolution; j++) { board.InlandWaterConnectivity[i][j] = new TileForInlandWaterConnectivity() { x = i, y = j, isWater = board.RiverMap[i][j] }; } } foreach (var borderLine in borderLines) { var line = new WaterBorderLine() { Points = borderLine.Select(p => new Microsoft.Xna.Framework.Point(p.x, p.y)).ToList() }; board.BorderLines.Add(line); foreach (var p in line.Points) { board.InlandWaterConnectivity[p.X][p.Y].WaterBorderLines.Add(line); } } #if DEBUG ImageWriter.WaterWriteImage(board.RiverMap, board.ElevationMap, resolution, "C:\\11\\RiverMap.png", new Color(1, 0, 0, 1f)); ImageWriter.WaterWriteImage(board.RiverBorderMap, board.ElevationMap, resolution, "C:\\11\\RiverBorderMap.png", new Color(1, 0, 0, 1f)); ImageWriter.RiverBordersWriteImage(borderLines, board.ElevationMap, resolution, "C:\\11\\riverBordersLines.png"); #endif for (int x = 0; x < game.WorldSettings.WorldBoardWidth; x++) { for (int y = 0; y < game.WorldSettings.WorldBoardHeight; y++) { var worldTile = board.WorldTiles[x, y]; if (board.RiverMap[x][y] && worldTile.Terrain != TerrainTypes.Water) { worldTile.Terrain = TerrainTypes.Water; worldTile.Biome = Biomes.River; } } } }