private void GenerateJaggedLines() { foreach (LocationConnection connection in connections) { Vector2 start = connection.Locations[0].MapPosition; Vector2 end = connection.Locations[1].MapPosition; int generations = (int)(Math.Sqrt(Vector2.Distance(start, end) / 10.0f)); //connection.CrackSegments = MathUtils.GenerateJaggedLine(start, end, generations, 5.0f); connection.CrackSegments = MathUtils.GenerateJaggedLine(start, end, generations, 0.0f); } }
partial void GenerateLocationConnectionVisuals() { foreach (LocationConnection connection in Connections) { Vector2 connectionStart = connection.Locations[0].MapPosition; Vector2 connectionEnd = connection.Locations[1].MapPosition; float connectionLength = Vector2.Distance(connectionStart, connectionEnd); int iterations = Math.Min((int)Math.Sqrt(connectionLength * generationParams.ConnectionIndicatorIterationMultiplier), 5); connection.CrackSegments.Clear(); connection.CrackSegments.AddRange(MathUtils.GenerateJaggedLine( connectionStart, connectionEnd, iterations, connectionLength * generationParams.ConnectionIndicatorDisplacementMultiplier)); } }
partial void GenerateNoiseMapProjSpecific() { if (noiseTexture == null) { noiseTexture = new Texture2D(GameMain.Instance.GraphicsDevice, generationParams.NoiseResolution, generationParams.NoiseResolution); rawNoiseTexture = new Texture2D(GameMain.Instance.GraphicsDevice, generationParams.NoiseResolution, generationParams.NoiseResolution); rawNoiseSprite = new Sprite(rawNoiseTexture, null, null); } Color[] crackTextureData = new Color[generationParams.NoiseResolution * generationParams.NoiseResolution]; Color[] noiseTextureData = new Color[generationParams.NoiseResolution * generationParams.NoiseResolution]; Color[] rawNoiseTextureData = new Color[generationParams.NoiseResolution * generationParams.NoiseResolution]; for (int x = 0; x < generationParams.NoiseResolution; x++) { for (int y = 0; y < generationParams.NoiseResolution; y++) { noiseTextureData[x + y * generationParams.NoiseResolution] = Color.Lerp(Color.Black, Color.Transparent, Noise[x, y]); rawNoiseTextureData[x + y * generationParams.NoiseResolution] = Color.Lerp(Color.Black, Color.White, Rand.Range(0.0f, 1.0f)); } } float mapRadius = size / 2; Vector2 mapCenter = Vector2.One * mapRadius; foreach (LocationConnection connection in connections) { float centerDist = Vector2.Distance(connection.CenterPos, mapCenter); Vector2 connectionStart = connection.Locations[0].MapPosition; Vector2 connectionEnd = connection.Locations[1].MapPosition; float connectionLength = Vector2.Distance(connectionStart, connectionEnd); int iterations = (int)(Math.Sqrt(connectionLength * generationParams.ConnectionIndicatorIterationMultiplier)); connection.CrackSegments = MathUtils.GenerateJaggedLine( connectionStart, connectionEnd, iterations, connectionLength * generationParams.ConnectionIndicatorDisplacementMultiplier); iterations = (int)(Math.Sqrt(connectionLength * generationParams.ConnectionIterationMultiplier)); var visualCrackSegments = MathUtils.GenerateJaggedLine( connectionStart, connectionEnd, iterations, connectionLength * generationParams.ConnectionDisplacementMultiplier); float totalLength = Vector2.Distance(visualCrackSegments[0][0], visualCrackSegments.Last()[1]); for (int i = 0; i < visualCrackSegments.Count; i++) { Vector2 start = visualCrackSegments[i][0] * (generationParams.NoiseResolution / (float)size); Vector2 end = visualCrackSegments[i][1] * (generationParams.NoiseResolution / (float)size); float length = Vector2.Distance(start, end); for (float x = 0; x < 1; x += 1.0f / length) { Vector2 pos = Vector2.Lerp(start, end, x); SetNoiseColorOnArea(pos, MathHelper.Clamp((int)(totalLength / 30), 2, 5) + Rand.Range(-1, 1), Color.Transparent); } } } void SetNoiseColorOnArea(Vector2 pos, int dist, Color color) { for (int x = -dist; x < dist; x++) { for (int y = -dist; y < dist; y++) { float d = 1.0f - new Vector2(x, y).Length() / dist; if (d <= 0) { continue; } int xIndex = (int)pos.X + x; if (xIndex < 0 || xIndex >= generationParams.NoiseResolution) { continue; } int yIndex = (int)pos.Y + y; if (yIndex < 0 || yIndex >= generationParams.NoiseResolution) { continue; } float perlin = (float)PerlinNoise.CalculatePerlin( xIndex / (float)generationParams.NoiseResolution * 100.0f, yIndex / (float)generationParams.NoiseResolution * 100.0f, 0); byte a = Math.Max(crackTextureData[xIndex + yIndex * generationParams.NoiseResolution].A, (byte)((d * perlin) * 255)); crackTextureData[xIndex + yIndex * generationParams.NoiseResolution].A = a; } } } for (int i = 0; i < noiseTextureData.Length; i++) { float darken = noiseTextureData[i].A / 255.0f; Color pathColor = Color.Lerp(Color.White, Color.Transparent, noiseTextureData[i].A / 255.0f); noiseTextureData[i] = Color.Lerp(noiseTextureData[i], pathColor, crackTextureData[i].A / 255.0f * 0.5f); } noiseTexture.SetData(noiseTextureData); rawNoiseTexture.SetData(rawNoiseTextureData); }
private void GenerateLocations() { Voronoi voronoi = new Voronoi(0.5f); List <Vector2> sites = new List <Vector2>(); for (int i = 0; i < 100; i++) { sites.Add(new Vector2(Rand.Range(0.0f, size, Rand.RandSync.Server), Rand.Range(0.0f, size, Rand.RandSync.Server))); } List <GraphEdge> edges = voronoi.MakeVoronoiGraph(sites, size, size); sites.Clear(); foreach (GraphEdge edge in edges) { if (edge.point1 == edge.point2) { continue; } //remove points from the edge of the map if (edge.point1.X == 0 || edge.point1.X == size) { continue; } if (edge.point1.Y == 0 || edge.point1.Y == size) { continue; } if (edge.point2.X == 0 || edge.point2.X == size) { continue; } if (edge.point2.Y == 0 || edge.point2.Y == size) { continue; } Location[] newLocations = new Location[2]; newLocations[0] = locations.Find(l => l.MapPosition == edge.point1 || l.MapPosition == edge.point2); newLocations[1] = locations.Find(l => l != newLocations[0] && (l.MapPosition == edge.point1 || l.MapPosition == edge.point2)); for (int i = 0; i < 2; i++) { if (newLocations[i] != null) { continue; } Vector2[] points = new Vector2[] { edge.point1, edge.point2 }; int positionIndex = Rand.Int(1, Rand.RandSync.Server); Vector2 position = points[positionIndex]; if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) { position = points[1 - positionIndex]; } newLocations[i] = Location.CreateRandom(position); locations.Add(newLocations[i]); } //int seed = (newLocations[0].GetHashCode() | newLocations[1].GetHashCode()); connections.Add(new LocationConnection(newLocations[0], newLocations[1])); } //remove connections that are too short float minDistance = 50.0f; for (int i = connections.Count - 1; i >= 0; i--) { LocationConnection connection = connections[i]; if (Vector2.Distance(connection.Locations[0].MapPosition, connection.Locations[1].MapPosition) > minDistance) { continue; } locations.Remove(connection.Locations[0]); connections.Remove(connection); foreach (LocationConnection connection2 in connections) { if (connection2.Locations[0] == connection.Locations[0]) { connection2.Locations[0] = connection.Locations[1]; } if (connection2.Locations[1] == connection.Locations[0]) { connection2.Locations[1] = connection.Locations[1]; } } } foreach (LocationConnection connection in connections) { connection.Locations[0].Connections.Add(connection); connection.Locations[1].Connections.Add(connection); } for (int i = connections.Count - 1; i >= 0; i--) { i = Math.Min(i, connections.Count - 1); LocationConnection connection = connections[i]; for (int n = Math.Min(i - 1, connections.Count - 1); n >= 0; n--) { if (connection.Locations.Contains(connections[n].Locations[0]) && connection.Locations.Contains(connections[n].Locations[1])) { connections.RemoveAt(n); } } } foreach (LocationConnection connection in connections) { Vector2 start = connection.Locations[0].MapPosition; Vector2 end = connection.Locations[1].MapPosition; int generations = (int)(Math.Sqrt(Vector2.Distance(start, end) / 10.0f)); connection.CrackSegments = MathUtils.GenerateJaggedLine(start, end, generations, 5.0f); } AssignBiomes(); }
public void Generate(bool mirror = false) { Stopwatch sw = new Stopwatch(); sw.Start(); if (loaded != null) { loaded.Unload(); } loaded = this; positionsOfInterest = new List <InterestingPosition>(); renderer = new LevelRenderer(this); Voronoi voronoi = new Voronoi(1.0); List <Vector2> sites = new List <Vector2>(); bodies = new List <Body>(); Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); backgroundColor = generationParams.BackgroundColor; float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3; GameMain.LightManager.AmbientLight = new Color(backgroundColor * (10.0f / avgValue), 1.0f); float minWidth = 6500.0f; if (Submarine.MainSub != null) { Rectangle dockedSubBorders = Submarine.MainSub.GetDockedBorders(); minWidth = Math.Max(minWidth, Math.Max(dockedSubBorders.Width, dockedSubBorders.Height)); } startPosition = new Vector2( Rand.Range(minWidth, minWidth * 2, false), Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, false)); endPosition = new Vector2( borders.Width - Rand.Range(minWidth, minWidth * 2, false), Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, false)); List <Vector2> pathNodes = new List <Vector2>(); Rectangle pathBorders = borders;// new Rectangle((int)minWidth, (int)minWidth, borders.Width - (int)minWidth * 2, borders.Height - (int)minWidth); pathBorders.Inflate(-minWidth * 2, -minWidth * 2); pathNodes.Add(new Vector2(startPosition.X, borders.Height)); Vector2 nodeInterval = generationParams.MainPathNodeIntervalRange; for (float x = startPosition.X + Rand.Range(nodeInterval.X, nodeInterval.Y, false); x < endPosition.X - Rand.Range(nodeInterval.X, nodeInterval.Y, false); x += Rand.Range(nodeInterval.X, nodeInterval.Y, false)) { pathNodes.Add(new Vector2(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, false))); } pathNodes.Add(new Vector2(endPosition.X, borders.Height)); if (pathNodes.Count <= 2) { pathNodes.Add((startPosition + endPosition) / 2); } List <List <Vector2> > smallTunnels = new List <List <Vector2> >(); for (int i = 0; i < generationParams.SmallTunnelCount; i++) { var tunnelStartPos = pathNodes[Rand.Range(2, pathNodes.Count - 2, false)]; tunnelStartPos.X = MathHelper.Clamp(tunnelStartPos.X, pathBorders.X, pathBorders.Right); float tunnelLength = Rand.Range( generationParams.SmallTunnelLengthRange.X, generationParams.SmallTunnelLengthRange.Y, false); var tunnelNodes = MathUtils.GenerateJaggedLine( tunnelStartPos, new Vector2(tunnelStartPos.X, pathBorders.Bottom) + Rand.Vector(tunnelLength, false), 4, 1000.0f); List <Vector2> tunnel = new List <Vector2>(); foreach (Vector2[] tunnelNode in tunnelNodes) { if (!pathBorders.Contains(tunnelNode[0])) { continue; } tunnel.Add(tunnelNode[0]); } if (tunnel.Any()) { smallTunnels.Add(tunnel); } } Vector2 siteInterval = generationParams.VoronoiSiteInterval; Vector2 siteVariance = generationParams.VoronoiSiteVariance; for (float x = siteInterval.X / 2; x < borders.Width; x += siteInterval.X) { for (float y = siteInterval.Y / 2; y < borders.Height; y += siteInterval.Y) { Vector2 site = new Vector2( x + Rand.Range(-siteVariance.X, siteVariance.X, false), y + Rand.Range(-siteVariance.Y, siteVariance.Y, false)); if (smallTunnels.Any(t => t.Any(node => Vector2.Distance(node, site) < siteInterval.Length()))) { //add some more sites around the small tunnels to generate more small voronoi cells if (x < borders.Width - siteInterval.X) { sites.Add(new Vector2(x, y) + Vector2.UnitX * siteInterval * 0.5f); } if (y < borders.Height - siteInterval.Y) { sites.Add(new Vector2(x, y) + Vector2.UnitY * siteInterval * 0.5f); } if (x < borders.Width - siteInterval.X && y < borders.Height - siteInterval.Y) { sites.Add(new Vector2(x, y) + Vector2.One * siteInterval * 0.5f); } } if (mirror) { site.X = borders.Width - site.X; } sites.Add(site); } } Stopwatch sw2 = new Stopwatch(); sw2.Start(); List <GraphEdge> graphEdges = voronoi.MakeVoronoiGraph(sites, borders.Width, borders.Height); Debug.WriteLine("MakeVoronoiGraph: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); //construct voronoi cells based on the graph edges cells = CaveGenerator.GraphEdgesToCells(graphEdges, borders, GridCellSize, out cellGrid); Debug.WriteLine("find cells: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); List <VoronoiCell> mainPath = CaveGenerator.GeneratePath(pathNodes, cells, cellGrid, GridCellSize, new Rectangle(pathBorders.X, pathBorders.Y, pathBorders.Width, borders.Height), 0.5f, mirror); for (int i = 2; i < mainPath.Count; i += 3) { positionsOfInterest.Add(new InterestingPosition(mainPath[i].Center, PositionType.MainPath)); } List <VoronoiCell> pathCells = new List <VoronoiCell>(mainPath); EnlargeMainPath(pathCells, minWidth); foreach (InterestingPosition positionOfInterest in positionsOfInterest) { WayPoint wayPoint = new WayPoint(positionOfInterest.Position, SpawnType.Enemy, null); wayPoint.MoveWithLevel = true; } startPosition.X = pathCells[0].Center.X; foreach (List <Vector2> tunnel in smallTunnels) { if (tunnel.Count < 2) { continue; } //find the cell which the path starts from int startCellIndex = CaveGenerator.FindCellIndex(tunnel[0], cells, cellGrid, GridCellSize, 1); if (startCellIndex < 0) { continue; } //if it wasn't one of the cells in the main path, don't create a tunnel if (cells[startCellIndex].CellType != CellType.Path) { continue; } var newPathCells = CaveGenerator.GeneratePath(tunnel, cells, cellGrid, GridCellSize, pathBorders); positionsOfInterest.Add(new InterestingPosition(tunnel.Last(), PositionType.Cave)); if (tunnel.Count > 4) { positionsOfInterest.Add(new InterestingPosition(tunnel[tunnel.Count / 2], PositionType.Cave)); } pathCells.AddRange(newPathCells); } Debug.WriteLine("path: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); cells = CleanCells(pathCells); pathCells.AddRange(CreateBottomHoles(generationParams.BottomHoleProbability, new Rectangle( (int)(borders.Width * 0.2f), 0, (int)(borders.Width * 0.6f), (int)(borders.Height * 0.8f)))); foreach (VoronoiCell cell in cells) { if (cell.Center.Y < borders.Height / 2) { continue; } cell.edges.ForEach(e => e.OutsideLevel = true); } foreach (VoronoiCell cell in pathCells) { cell.edges.ForEach(e => e.OutsideLevel = false); cell.CellType = CellType.Path; cells.Remove(cell); } //generate some narrow caves int caveAmount = 0;// Rand.Int(3, false); List <VoronoiCell> usedCaveCells = new List <VoronoiCell>(); for (int i = 0; i < caveAmount; i++) { Vector2 startPoint = Vector2.Zero; VoronoiCell startCell = null; var caveCells = new List <VoronoiCell>(); int maxTries = 5, tries = 0; while (tries < maxTries) { startCell = cells[Rand.Int(cells.Count, false)]; //find an edge between the cell and the already carved path GraphEdge startEdge = startCell.edges.Find(e => pathCells.Contains(e.AdjacentCell(startCell))); if (startEdge != null) { startPoint = (startEdge.point1 + startEdge.point2) / 2.0f; startPoint += startPoint - startCell.Center; //get the cells in which the cave will be carved caveCells = GetCells(startCell.Center, 2); //remove cells that have already been "carved" out caveCells.RemoveAll(c => c.CellType == CellType.Path); //if any of the cells have already been used as a cave, continue and find some other cells if (usedCaveCells.Any(c => caveCells.Contains(c))) { continue; } break; } tries++; } //couldn't find a place for a cave -> abort if (tries >= maxTries) { break; } if (!caveCells.Any()) { continue; } usedCaveCells.AddRange(caveCells); List <VoronoiCell> caveSolidCells; var cavePathCells = CaveGenerator.CarveCave(caveCells, startPoint, out caveSolidCells); //remove the large cells used as a "base" for the cave (they've now been replaced with smaller ones) caveCells.ForEach(c => cells.Remove(c)); cells.AddRange(caveSolidCells); foreach (VoronoiCell cell in cavePathCells) { cells.Remove(cell); } pathCells.AddRange(cavePathCells); for (int j = cavePathCells.Count / 2; j < cavePathCells.Count; j += 10) { positionsOfInterest.Add(new InterestingPosition(cavePathCells[j].Center, PositionType.Cave)); } } for (int x = 0; x < cellGrid.GetLength(0); x++) { for (int y = 0; y < cellGrid.GetLength(1); y++) { cellGrid[x, y].Clear(); } } foreach (VoronoiCell cell in cells) { int x = (int)Math.Floor(cell.Center.X / GridCellSize); int y = (int)Math.Floor(cell.Center.Y / GridCellSize); if (x < 0 || y < 0 || x >= cellGrid.GetLength(0) || y >= cellGrid.GetLength(1)) { continue; } cellGrid[x, y].Add(cell); } ruins = new List <Ruin>(); for (int i = 0; i < generationParams.RuinCount; i++) { GenerateRuin(mainPath); } startPosition.Y = borders.Height; endPosition.Y = borders.Height; List <VoronoiCell> cellsWithBody = new List <VoronoiCell>(cells); List <VertexPositionTexture> bodyVertices; bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out bodyVertices); renderer.SetBodyVertices(bodyVertices.ToArray()); renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells)); renderer.PlaceSprites(generationParams.BackgroundSpriteAmount); ShaftBody = BodyFactory.CreateEdge(GameMain.World, ConvertUnits.ToSimUnits(new Vector2(borders.X, 0)), ConvertUnits.ToSimUnits(new Vector2(borders.Right, 0))); ShaftBody.SetTransform(ConvertUnits.ToSimUnits(new Vector2(0.0f, borders.Height)), 0.0f); ShaftBody.BodyType = BodyType.Static; ShaftBody.CollisionCategories = Physics.CollisionLevel; bodies.Add(ShaftBody); foreach (VoronoiCell cell in cells) { foreach (GraphEdge edge in cell.edges) { edge.cell1 = null; edge.cell2 = null; edge.site1 = null; edge.site2 = null; } } //initialize MapEntities that aren't in any sub (e.g. items inside ruins) MapEntity.MapLoaded(null); Debug.WriteLine("Generatelevel: " + sw2.ElapsedMilliseconds + " ms"); sw2.Restart(); if (mirror) { Vector2 temp = startPosition; startPosition = endPosition; endPosition = temp; } Debug.WriteLine("**********************************************************************************"); Debug.WriteLine("Generated a map with " + sites.Count + " sites in " + sw.ElapsedMilliseconds + " ms"); Debug.WriteLine("Seed: " + seed); Debug.WriteLine("**********************************************************************************"); }