public void PlaceObjects(Level level, int amount) { objectGrid = new List <LevelObject> [ level.Size.X / GridSize, (level.Size.Y - level.BottomPos) / GridSize]; List <SpawnPosition> availableSpawnPositions = new List <SpawnPosition>(); var levelCells = level.GetAllCells(); availableSpawnPositions.AddRange(GetAvailableSpawnPositions(levelCells, LevelObjectPrefab.SpawnPosType.Wall)); availableSpawnPositions.AddRange(GetAvailableSpawnPositions(level.SeaFloor.Cells, LevelObjectPrefab.SpawnPosType.SeaFloor)); foreach (Structure structure in Structure.WallList) { if (!structure.HasBody || structure.HiddenInGame) { continue; } if (level.Ruins.Any(r => r.Submarine == structure.Submarine)) { if (structure.IsHorizontal) { bool topHull = Hull.FindHull(structure.WorldPosition + Vector2.UnitY * 64) != null; bool bottomHull = Hull.FindHull(structure.WorldPosition - Vector2.UnitY * 64) != null; if (topHull && bottomHull) { continue; } availableSpawnPositions.Add(new SpawnPosition( new GraphEdge(new Vector2(structure.WorldRect.X, structure.WorldPosition.Y), new Vector2(structure.WorldRect.Right, structure.WorldPosition.Y)), bottomHull ? Vector2.UnitY : -Vector2.UnitY, LevelObjectPrefab.SpawnPosType.RuinWall, bottomHull ? Alignment.Bottom : Alignment.Top)); } else { bool rightHull = Hull.FindHull(structure.WorldPosition + Vector2.UnitX * 64) != null; bool leftHull = Hull.FindHull(structure.WorldPosition - Vector2.UnitX * 64) != null; if (rightHull && leftHull) { continue; } availableSpawnPositions.Add(new SpawnPosition( new GraphEdge(new Vector2(structure.WorldPosition.X, structure.WorldRect.Y), new Vector2(structure.WorldPosition.X, structure.WorldRect.Y - structure.WorldRect.Height)), leftHull ? Vector2.UnitX : -Vector2.UnitX, LevelObjectPrefab.SpawnPosType.RuinWall, leftHull ? Alignment.Left : Alignment.Right)); } } } foreach (var posOfInterest in level.PositionsOfInterest) { if (posOfInterest.PositionType != Level.PositionType.MainPath && posOfInterest.PositionType != Level.PositionType.SidePath) { continue; } availableSpawnPositions.Add(new SpawnPosition( new GraphEdge(posOfInterest.Position.ToVector2(), posOfInterest.Position.ToVector2() + Vector2.UnitX), Vector2.UnitY, LevelObjectPrefab.SpawnPosType.MainPath, Alignment.Top)); } availableSpawnPositions.Add(new SpawnPosition( new GraphEdge(level.StartPosition - Vector2.UnitX, level.StartPosition + Vector2.UnitX), -Vector2.UnitY, LevelObjectPrefab.SpawnPosType.LevelStart, Alignment.Top)); availableSpawnPositions.Add(new SpawnPosition( new GraphEdge(level.EndPosition - Vector2.UnitX, level.EndPosition + Vector2.UnitX), -Vector2.UnitY, LevelObjectPrefab.SpawnPosType.LevelEnd, Alignment.Top)); var availablePrefabs = new List <LevelObjectPrefab>(LevelObjectPrefab.List); objects = new List <LevelObject>(); updateableObjects = new List <LevelObject>(); Dictionary <LevelObjectPrefab, List <SpawnPosition> > suitableSpawnPositions = new Dictionary <LevelObjectPrefab, List <SpawnPosition> >(); Dictionary <LevelObjectPrefab, List <float> > spawnPositionWeights = new Dictionary <LevelObjectPrefab, List <float> >(); for (int i = 0; i < amount; i++) { //get a random prefab and find a place to spawn it LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams, availablePrefabs); if (prefab == null) { continue; } if (!suitableSpawnPositions.ContainsKey(prefab)) { float minDistance = level.Size.X * 0.2f; suitableSpawnPositions.Add(prefab, availableSpawnPositions.Where(sp => sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) && sp.Length >= prefab.MinSurfaceWidth && (prefab.AllowAtStart || !level.IsCloseToStart(sp.GraphEdge.Center, minDistance)) && (prefab.AllowAtEnd || !level.IsCloseToEnd(sp.GraphEdge.Center, minDistance)) && (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 (prefab.MaxCount < amount) { if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount) { availablePrefabs.Remove(prefab); } } } foreach (Level.Cave cave in level.Caves) { availablePrefabs = new List <LevelObjectPrefab>(LevelObjectPrefab.List.FindAll(p => p.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.CaveWall))); availableSpawnPositions.Clear(); suitableSpawnPositions.Clear(); spawnPositionWeights.Clear(); 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 (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 < cave.CaveGenerationParams.LevelObjectAmount; i++) { //get a random prefab and find a place to spawn it LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: true); 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, cave); if (prefab.MaxCount < amount) { if (objects.Count(o => o.Prefab == prefab && o.ParentCave == cave) >= prefab.MaxCount) { availablePrefabs.Remove(prefab); } } } } }