public static LocationType Random(string seed = "", int?zone = null) { Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes"); if (!string.IsNullOrWhiteSpace(seed)) { Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); } List <LocationType> allowedLocationTypes = zone.HasValue ? List.FindAll(lt => lt.CommonnessPerZone.ContainsKey(zone.Value)) : List; if (allowedLocationTypes.Count == 0) { DebugConsole.ThrowError("Could not generate a random location type - no location types for the zone " + zone + " found!"); } if (zone.HasValue) { return(ToolBox.SelectWeightedRandom( allowedLocationTypes, allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(), Rand.RandSync.Server)); } else { return(allowedLocationTypes[Rand.Int(allowedLocationTypes.Count, Rand.RandSync.Server)]); } }
public static LevelGenerationParams GetRandom(string seed, Biome biome = null) { Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); if (levelParams == null || !levelParams.Any()) { DebugConsole.ThrowError("Level generation presets not found - using default presets"); return(new LevelGenerationParams(null)); } if (biome == null) { return(levelParams.GetRandom(lp => lp.allowedBiomes.Count > 0, Rand.RandSync.Server)); } var matchingLevelParams = levelParams.FindAll(lp => lp.allowedBiomes.Contains(biome)); if (matchingLevelParams.Count == 0) { DebugConsole.ThrowError("Level generation presets not found for the biome \"" + biome.Identifier + "\"!"); return(new LevelGenerationParams(null)); } return(matchingLevelParams[Rand.Range(0, matchingLevelParams.Count, Rand.RandSync.Server)]); }
private void CreateEditor() { editor = new GUIFrame(new RectTransform(new Vector2(0.25f, 1.0f), GUI.Canvas, Anchor.TopRight, minSize: new Point(400, 0))); var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), editor.RectTransform, Anchor.Center)) { Stretch = true, RelativeSpacing = 0.02f, CanBeFocused = false }; var listBox = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.95f), paddedFrame.RectTransform, Anchor.Center)); new SerializableEntityEditor(listBox.Content.RectTransform, generationParams, false, true); new GUIButton(new RectTransform(new Vector2(1.0f, 0.05f), paddedFrame.RectTransform), "Generate") { OnClicked = (btn, userData) => { Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed)); Generate(); InitProjectSpecific(); return(true); } }; }
public Map(string seed, int size) { this.seed = seed; this.size = size; levels = new List<Level>(); locations = new List<Location>(); connections = new List<LocationConnection>(); if (iceTexture == null) iceTexture = new Sprite("Content/Map/iceSurface.png", Vector2.Zero); if (iceCraters == null) iceCraters = TextureLoader.FromFile("Content/Map/iceCraters.png"); if (iceCrack == null) iceCrack = TextureLoader.FromFile("Content/Map/iceCrack.png"); Rand.SetSyncedSeed(ToolBox.StringToInt(this.seed)); GenerateMap(CONNECTION.VORONOI); //GenerateLocations(); /*currentLocation = locations[locations.Count / 2]; currentLocation.Discovered = true; GenerateDifficulties(currentLocation, new List<LocationConnection> (connections), 10.0f);*/ foreach (LocationConnection connection in connections) { connection.Level = Level.CreateRandom(connection); } }
/// <summary> /// Generate a new campaign map from the seed /// </summary> public Map(CampaignMode campaign, string seed) : this() { Seed = seed; Rand.SetSyncedSeed(ToolBox.StringToInt(Seed)); Generate(); if (Locations.Count == 0) { throw new Exception($"Generating a campaign map failed (no locations created). Width: {Width}, height: {Height}"); } for (int i = 0; i < Locations.Count; i++) { Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server)); } foreach (Location location in Locations) { if (!location.Type.Identifier.Equals("city", StringComparison.OrdinalIgnoreCase) && !location.Type.Identifier.Equals("outpost", StringComparison.OrdinalIgnoreCase)) { continue; } if (CurrentLocation == null || location.MapPosition.X < CurrentLocation.MapPosition.X) { CurrentLocation = StartLocation = furthestDiscoveredLocation = location; } } CurrentLocation.CreateStore(); CurrentLocation.Discovered = true; InitProjectSpecific(); }
public static LevelData CreateRandom(string seed = "", float?difficulty = null, LevelGenerationParams generationParams = null) { if (string.IsNullOrEmpty(seed)) { seed = Rand.Range(0, int.MaxValue, Rand.RandSync.Server).ToString(); } Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); LevelType type = generationParams == null ? LevelData.LevelType.LocationConnection : generationParams.Type; if (generationParams == null) { generationParams = LevelGenerationParams.GetRandom(seed, type); } var biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ?? LevelGenerationParams.GetBiomes().GetRandom(Rand.RandSync.Server); return(new LevelData( seed, difficulty ?? Rand.Range(30.0f, 80.0f, Rand.RandSync.Server), Rand.Range(0.0f, 1.0f, Rand.RandSync.Server), generationParams, biome)); }
public static LevelData CreateRandom(string seed = "", float?difficulty = null, LevelGenerationParams generationParams = null) { if (string.IsNullOrEmpty(seed)) { seed = Rand.Range(0, int.MaxValue, Rand.RandSync.Server).ToString(); } Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); LevelType type = generationParams == null ? LevelData.LevelType.LocationConnection : generationParams.Type; if (generationParams == null) { generationParams = LevelGenerationParams.GetRandom(seed, type); } var biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => generationParams.AllowedBiomes.Contains(b)) ?? LevelGenerationParams.GetBiomes().GetRandom(Rand.RandSync.Server); var levelData = new LevelData( seed, difficulty ?? Rand.Range(30.0f, 80.0f, Rand.RandSync.Server), Rand.Range(0.0f, 1.0f, Rand.RandSync.Server), generationParams, biome); if (type == LevelType.LocationConnection) { float beaconRng = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server); levelData.HasBeaconStation = beaconRng < 0.5f; levelData.IsBeaconActive = beaconRng > 0.25f; } GameMain.GameSession?.GameMode?.Mission?.AdjustLevelData(levelData); return(levelData); }
private Character SpawnWatchman(Submarine outpost) { WayPoint watchmanSpawnpoint = WayPoint.WayPointList.Find(wp => wp.Submarine == outpost); if (watchmanSpawnpoint == null) { DebugConsole.ThrowError("Failed to spawn a watchman at the outpost. No spawnpoints found inside the outpost."); return(null); } string seed = outpost == Level.Loaded.StartOutpost ? map.SelectedLocation.Name : map.CurrentLocation.Name; Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); JobPrefab watchmanJob = JobPrefab.List.Find(jp => jp.Identifier == "watchman"); CharacterInfo characterInfo = new CharacterInfo(Character.HumanConfigFile, jobPrefab: watchmanJob); var spawnedCharacter = Character.Create(characterInfo, watchmanSpawnpoint.WorldPosition, Level.Loaded.Seed + (outpost == Level.Loaded.StartOutpost ? "start" : "end")); InitializeWatchman(spawnedCharacter); (spawnedCharacter.AIController as HumanAIController)?.ObjectiveManager.SetOrder( new AIObjectiveGoTo(watchmanSpawnpoint, spawnedCharacter, repeat: true, getDivingGearIfNeeded: false)); if (watchmanJob != null) { spawnedCharacter.GiveJobItems(); } return(spawnedCharacter); }
public static Level CreateRandom(string seed = "") { if (seed == "") { seed = Rand.Range(0, int.MaxValue, Rand.RandSync.Server).ToString(); } Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); return(new Level(seed, Rand.Range(30.0f, 80.0f, Rand.RandSync.Server), LevelGenerationParams.GetRandom(seed))); }
public static LevelGenerationParams GetRandom(string seed) { Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); if (presets == null || !presets.Any()) { DebugConsole.ThrowError("Level generation presets not found - using default presets"); return(new LevelGenerationParams(null)); } return(presets[Rand.Range(0, presets.Count, Rand.RandSync.Server)]); }
public Map(string seed) { generationParams = MapGenerationParams.Instance; this.Seed = seed; this.size = generationParams.Size; levels = new List <Level>(); Locations = new List <Location>(); connections = new List <LocationConnection>(); Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed)); Generate(); //start from the colony furthest away from the center float largestDist = 0.0f; Vector2 center = new Vector2(size, size) / 2; foreach (Location location in Locations) { if (location.Type.Identifier != "City") { continue; } float dist = Vector2.DistanceSquared(center, location.MapPosition); if (dist > largestDist) { largestDist = dist; CurrentLocation = location; } } CurrentLocation.Discovered = true; foreach (LocationConnection connection in connections) { connection.Level = Level.CreateRandom(connection); } InitProjectSpecific(); }
public static void PlaceIfNeeded() { if (GameMain.NetworkMember != null && !GameMain.NetworkMember.IsServer) { return; } for (int i = 0; i < Submarine.MainSubs.Length; i++) { if (Submarine.MainSubs[i] == null || Submarine.MainSubs[i].Info.InitialSuppliesSpawned) { continue; } List <Submarine> subs = new List <Submarine>() { Submarine.MainSubs[i] }; subs.AddRange(Submarine.MainSubs[i].DockedTo.Where(d => !d.Info.IsOutpost)); Place(subs); subs.ForEach(s => s.Info.InitialSuppliesSpawned = true); } float difficultyModifier = GetLevelDifficultyModifier(); foreach (var sub in Submarine.Loaded) { if (sub.Info.Type == SubmarineType.Player || sub.Info.Type == SubmarineType.Outpost || sub.Info.Type == SubmarineType.OutpostModule || sub.Info.Type == SubmarineType.EnemySubmarine) { continue; } Place(sub.ToEnumerable(), difficultyModifier: difficultyModifier); } if (Level.Loaded?.StartOutpost != null && Level.Loaded.Type == LevelData.LevelType.Outpost) { Rand.SetSyncedSeed(ToolBox.StringToInt(Level.Loaded.StartOutpost.Info.Name)); Place(Level.Loaded.StartOutpost.ToEnumerable()); } }
public static LocationType Random(string seed = "") { Debug.Assert(list.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes"); if (!string.IsNullOrWhiteSpace(seed)) { Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); } int randInt = Rand.Int(totalWeight, Rand.RandSync.Server); foreach (LocationType type in list) { if (randInt < type.commonness) { return(type); } randInt -= type.commonness; } return(null); }
private Character SpawnWatchman(Submarine outpost) { WayPoint watchmanSpawnpoint = WayPoint.WayPointList.Find(wp => wp.Submarine == outpost); if (watchmanSpawnpoint == null) { DebugConsole.ThrowError("Failed to spawn a watchman at the outpost. No spawnpoints found inside the outpost."); return(null); } string seed = outpost == Level.Loaded.StartOutpost ? map.SelectedLocation.Name : map.CurrentLocation.Name; Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); JobPrefab watchmanJob = JobPrefab.Get("watchman"); var variant = Rand.Range(0, watchmanJob.Variants, Rand.RandSync.Server); CharacterInfo characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: watchmanJob, variant: variant); var spawnedCharacter = Character.Create(characterInfo, watchmanSpawnpoint.WorldPosition, Level.Loaded.Seed + (outpost == Level.Loaded.StartOutpost ? "start" : "end")); InitializeWatchman(spawnedCharacter); var objectiveManager = (spawnedCharacter.AIController as HumanAIController)?.ObjectiveManager; if (objectiveManager != null) { var moveOrder = new AIObjectiveGoTo(watchmanSpawnpoint, spawnedCharacter, objectiveManager, repeat: true, getDivingGearIfNeeded: false); moveOrder.Completed += () => { // Turn towards the center of the sub. Doesn't work in all possible cases, but this is the simplest solution for now. spawnedCharacter.AnimController.TargetDir = spawnedCharacter.Submarine.WorldPosition.X > spawnedCharacter.WorldPosition.X ? Direction.Right : Direction.Left; }; objectiveManager.SetOrder(moveOrder); } if (watchmanJob != null) { spawnedCharacter.GiveJobItems(); } return(spawnedCharacter); }
public static LevelGenerationParams GetRandom(string seed, LevelData.LevelType type, Biome biome = null) { Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); if (LevelParams == null || !LevelParams.Any()) { DebugConsole.ThrowError("Level generation presets not found - using default presets"); return(new LevelGenerationParams(null)); } var matchingLevelParams = LevelParams.FindAll(lp => lp.Type == type && lp.allowedBiomes.Any()); if (biome == null) { matchingLevelParams = matchingLevelParams.FindAll(lp => !lp.allowedBiomes.Any(b => b.IsEndBiome)); } else { matchingLevelParams = matchingLevelParams.FindAll(lp => lp.allowedBiomes.Contains(biome)); } if (matchingLevelParams.Count == 0) { DebugConsole.ThrowError($"Suitable level generation presets not found (biome \"{(biome?.Identifier ?? "null")}\", type: \"{type}\"!"); if (biome != null) { //try to find params that at least have a suitable type matchingLevelParams = LevelParams.FindAll(lp => lp.Type == type); if (matchingLevelParams.Count == 0) { //still not found, give up and choose some params randomly matchingLevelParams = LevelParams; } } } return(matchingLevelParams[Rand.Range(0, matchingLevelParams.Count, Rand.RandSync.Server)]); }
public static void PlaceIfNeeded() { if (GameMain.NetworkMember != null && !GameMain.NetworkMember.IsServer) { return; } for (int i = 0; i < Submarine.MainSubs.Length; i++) { if (Submarine.MainSubs[i] == null || Submarine.MainSubs[i].Info.InitialSuppliesSpawned) { continue; } List <Submarine> subs = new List <Submarine>() { Submarine.MainSubs[i] }; subs.AddRange(Submarine.MainSubs[i].DockedTo.Where(d => !d.Info.IsOutpost)); Place(subs); subs.ForEach(s => s.Info.InitialSuppliesSpawned = true); } foreach (var wreck in Submarine.Loaded) { if (wreck.Info.IsWreck) { Place(wreck.ToEnumerable()); } } if (Level.Loaded?.StartOutpost != null && Level.Loaded.Type == LevelData.LevelType.Outpost) { Rand.SetSyncedSeed(ToolBox.StringToInt(Level.Loaded.StartOutpost.Info.Name)); Place(Level.Loaded.StartOutpost.ToEnumerable()); } }
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("**********************************************************************************"); }
public void PlaceNestObjects(Level level, Level.Cave cave, Vector2 nestPosition, float nestRadius, int objectAmount) { Rand.SetSyncedSeed(ToolBox.StringToInt(level.Seed)); var availablePrefabs = new List <LevelObjectPrefab>(LevelObjectPrefab.List.FindAll(p => p.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.NestWall))); Dictionary <LevelObjectPrefab, List <SpawnPosition> > suitableSpawnPositions = new Dictionary <LevelObjectPrefab, List <SpawnPosition> >(); Dictionary <LevelObjectPrefab, List <float> > spawnPositionWeights = new Dictionary <LevelObjectPrefab, List <float> >(); List <SpawnPosition> availableSpawnPositions = new List <SpawnPosition>(); var caveCells = cave.Tunnels.SelectMany(t => t.Cells); List <VoronoiCell> caveWallCells = new List <VoronoiCell>(); foreach (var edge in caveCells.SelectMany(c => c.Edges)) { if (!edge.NextToCave) { continue; } if (MathUtils.LineSegmentToPointDistanceSquared(edge.Point1.ToPoint(), edge.Point2.ToPoint(), nestPosition.ToPoint()) > nestRadius * nestRadius) { continue; } if (edge.Cell1?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell1); } if (edge.Cell2?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell2); } } availableSpawnPositions.AddRange(GetAvailableSpawnPositions(caveWallCells.Distinct(), LevelObjectPrefab.SpawnPosType.CaveWall)); for (int i = 0; i < objectAmount; i++) { //get a random prefab and find a place to spawn it LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: false); if (prefab == null) { continue; } if (!suitableSpawnPositions.ContainsKey(prefab)) { suitableSpawnPositions.Add(prefab, availableSpawnPositions.Where(sp => sp.Length >= prefab.MinSurfaceWidth && (sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList()); spawnPositionWeights.Add(prefab, suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList()); } SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server); if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; } PlaceObject(prefab, spawnPosition, level); if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount) { availablePrefabs.Remove(prefab); } } }
public void Generate(bool mirror = false) { if (backgroundSpriteManager == null) { var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.BackgroundSpritePrefabs); if (files.Count > 0) { backgroundSpriteManager = new BackgroundSpriteManager(files); } else { backgroundSpriteManager = new BackgroundSpriteManager("Content/BackgroundSprites/BackgroundSpritePrefabs.xml"); } } #if CLIENT if (backgroundCreatureManager == null) { var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.BackgroundCreaturePrefabs); if (files.Count > 0) { backgroundCreatureManager = new BackgroundCreatureManager(files); } else { backgroundCreatureManager = new BackgroundCreatureManager("Content/BackgroundSprites/BackgroundCreaturePrefabs.xml"); } } #endif Stopwatch sw = new Stopwatch(); sw.Start(); if (loaded != null) { loaded.Unload(); } loaded = this; positionsOfInterest = new List <InterestingPosition>(); Voronoi voronoi = new Voronoi(1.0); List <Vector2> sites = new List <Vector2>(); bodies = new List <Body>(); Rand.SetSyncedSeed(ToolBox.StringToInt(seed)); #if CLIENT renderer = new LevelRenderer(this); backgroundColor = generationParams.BackgroundColor; float avgValue = (backgroundColor.R + backgroundColor.G + backgroundColor.G) / 3; GameMain.LightManager.AmbientLight = new Color(backgroundColor * (10.0f / avgValue), 1.0f); #endif SeaFloorTopPos = generationParams.SeaFloorDepth + generationParams.MountainHeightMax + generationParams.SeaFloorVariance; float minWidth = 6500.0f; if (Submarine.MainSub != null) { Rectangle dockedSubBorders = Submarine.MainSub.GetDockedBorders(); minWidth = Math.Max(minWidth, Math.Max(dockedSubBorders.Width, dockedSubBorders.Height)); } Rectangle pathBorders = borders; pathBorders.Inflate(-minWidth * 2, -minWidth * 2); startPosition = new Vector2( Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server), Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, Rand.RandSync.Server)); endPosition = new Vector2( borders.Width - Rand.Range(minWidth, minWidth * 2, Rand.RandSync.Server), Rand.Range(borders.Height * 0.5f, borders.Height - minWidth * 2, Rand.RandSync.Server)); //---------------------------------------------------------------------------------- //generate the initial nodes for the main path and smaller tunnels //---------------------------------------------------------------------------------- List <Vector2> pathNodes = new List <Vector2>(); pathNodes.Add(new Vector2(startPosition.X, borders.Height)); Vector2 nodeInterval = generationParams.MainPathNodeIntervalRange; for (float x = startPosition.X + nodeInterval.X; x < endPosition.X - nodeInterval.X; x += Rand.Range(nodeInterval.X, nodeInterval.Y, Rand.RandSync.Server)) { pathNodes.Add(new Vector2(x, Rand.Range(pathBorders.Y, pathBorders.Bottom, Rand.RandSync.Server))); } pathNodes.Add(new Vector2(endPosition.X, borders.Height)); if (pathNodes.Count <= 2) { pathNodes.Insert(1, borders.Center.ToVector2()); } GenerateTunnels(pathNodes, minWidth); //---------------------------------------------------------------------------------- //generate voronoi sites //---------------------------------------------------------------------------------- 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, Rand.RandSync.Server), y + Rand.Range(-siteVariance.Y, siteVariance.Y, Rand.RandSync.Server)); 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); } } //---------------------------------------------------------------------------------- // construct the voronoi graph and cells //---------------------------------------------------------------------------------- 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(); //---------------------------------------------------------------------------------- // generate a path through the initial path nodes //---------------------------------------------------------------------------------- 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); //make sure the path is wide enough to pass through 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; //---------------------------------------------------------------------------------- // tunnels through the tunnel nodes //---------------------------------------------------------------------------------- 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(); //---------------------------------------------------------------------------------- // remove unnecessary cells and create some holes at the bottom of the level //---------------------------------------------------------------------------------- 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); } //---------------------------------------------------------------------------------- // initialize the cells that are still left and insert them into the cell grid //---------------------------------------------------------------------------------- foreach (VoronoiCell cell in pathCells) { cell.edges.ForEach(e => e.OutsideLevel = false); cell.CellType = CellType.Path; cells.Remove(cell); } 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); } //---------------------------------------------------------------------------------- // create some ruins //---------------------------------------------------------------------------------- ruins = new List <Ruin>(); for (int i = 0; i < generationParams.RuinCount; i++) { GenerateRuin(mainPath); } //---------------------------------------------------------------------------------- // generate the bodies and rendered triangles of the cells //---------------------------------------------------------------------------------- startPosition.Y = borders.Height; endPosition.Y = borders.Height; List <VoronoiCell> cellsWithBody = new List <VoronoiCell>(cells); List <Vector2[]> triangles; bodies = CaveGenerator.GeneratePolygons(cellsWithBody, out triangles); #if CLIENT renderer.SetBodyVertices(CaveGenerator.GenerateRenderVerticeList(triangles).ToArray(), generationParams.WallColor); renderer.SetWallVertices(CaveGenerator.GenerateWallShapes(cells), generationParams.WallColor); #endif TopBarrier = BodyFactory.CreateEdge(GameMain.World, ConvertUnits.ToSimUnits(new Vector2(borders.X, 0)), ConvertUnits.ToSimUnits(new Vector2(borders.Right, 0))); TopBarrier.SetTransform(ConvertUnits.ToSimUnits(new Vector2(0.0f, borders.Height)), 0.0f); TopBarrier.BodyType = BodyType.Static; TopBarrier.CollisionCategories = Physics.CollisionLevel; bodies.Add(TopBarrier); GenerateSeaFloor(); backgroundSpriteManager.PlaceSprites(this, generationParams.BackgroundSpriteAmount); #if CLIENT backgroundCreatureManager.SpawnSprites(80); #endif 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("**********************************************************************************"); if (GameSettings.VerboseLogging) { DebugConsole.NewMessage("Generated level with the seed " + seed + " (type: " + generationParams.Name + ")", Color.White); } }
/// <summary> /// Load a previously saved campaign map from XML /// </summary> private Map(CampaignMode campaign, XElement element) : this() { Seed = element.GetAttributeString("seed", "a"); Rand.SetSyncedSeed(ToolBox.StringToInt(Seed)); foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "location": int i = subElement.GetAttributeInt("i", 0); while (Locations.Count <= i) { Locations.Add(null); } Locations[i] = new Location(subElement); break; } } System.Diagnostics.Debug.Assert(!Locations.Contains(null)); for (int i = 0; i < Locations.Count; i++) { Locations[i].Reputation ??= new Reputation(campaign.CampaignMetadata, $"location.{i}", -100, 100, Rand.Range(-10, 10, Rand.RandSync.Server)); } foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "connection": Point locationIndices = subElement.GetAttributePoint("locations", new Point(0, 1)); if (locationIndices.X == locationIndices.Y) { continue; } var connection = new LocationConnection(Locations[locationIndices.X], Locations[locationIndices.Y]) { Passed = subElement.GetAttributeBool("passed", false), Difficulty = subElement.GetAttributeFloat("difficulty", 0.0f) }; Locations[locationIndices.X].Connections.Add(connection); Locations[locationIndices.Y].Connections.Add(connection); connection.LevelData = new LevelData(subElement.Element("Level")); string biomeId = subElement.GetAttributeString("biome", ""); connection.Biome = LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.Identifier == biomeId) ?? LevelGenerationParams.GetBiomes().FirstOrDefault(b => b.OldIdentifier == biomeId) ?? LevelGenerationParams.GetBiomes().First(); Connections.Add(connection); break; } } int startLocationindex = element.GetAttributeInt("startlocation", -1); if (startLocationindex > 0 && startLocationindex < Locations.Count) { StartLocation = Locations[startLocationindex]; } else { DebugConsole.AddWarning($"Error while loading the map. Start location index out of bounds (index: {startLocationindex}, location count: {Locations.Count})."); foreach (Location location in Locations) { if (!location.Type.HasOutpost) { continue; } if (StartLocation == null || location.MapPosition.X < StartLocation.MapPosition.X) { StartLocation = location; } } } int endLocationindex = element.GetAttributeInt("endlocation", -1); if (endLocationindex > 0 && endLocationindex < Locations.Count) { EndLocation = Locations[endLocationindex]; } else { DebugConsole.AddWarning($"Error while loading the map. End location index out of bounds (index: {endLocationindex}, location count: {Locations.Count})."); foreach (Location location in Locations) { if (EndLocation == null || location.MapPosition.X > EndLocation.MapPosition.X) { EndLocation = location; } } } InitProjectSpecific(); }