Beispiel #1
0
        public static void GeneratePath(Level.Tunnel tunnel, Level level)
        {
            var targetCells = new List <VoronoiCell>();

            for (int i = 0; i < tunnel.Nodes.Count; i++)
            {
                var closestCell = level.GetClosestCell(tunnel.Nodes[i].ToVector2());
                if (closestCell != null && !targetCells.Contains(closestCell))
                {
                    targetCells.Add(closestCell);
                }
            }
            tunnel.Cells.AddRange(GeneratePath(targetCells, level.GetAllCells()));
        }
        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 (RuinGeneration.Ruin ruin in level.Ruins)
            {
                foreach (var ruinShape in ruin.RuinShapes)
                {
                    foreach (var wall in ruinShape.Walls)
                    {
                        availableSpawnPositions.Add(new SpawnPosition(
                                                        new GraphEdge(wall.A, wall.B),
                                                        (wall.A + wall.B) / 2.0f - ruinShape.Center,
                                                        LevelObjectPrefab.SpawnPosType.RuinWall,
                                                        ruinShape.GetLineAlignment(wall)));
                    }
                }
            }

            foreach (var posOfInterest in level.PositionsOfInterest)
            {
                if (posOfInterest.PositionType != Level.PositionType.MainPath)
                {
                    continue;
                }

                availableSpawnPositions.Add(new SpawnPosition(
                                                new GraphEdge(posOfInterest.Position.ToVector2(), posOfInterest.Position.ToVector2() + Vector2.UnitX),
                                                Vector2.UnitY,
                                                LevelObjectPrefab.SpawnPosType.MainPath,
                                                Alignment.Top));
            }

            objects = new List <LevelObject>();
            for (int i = 0; i < amount; i++)
            {
                //get a random prefab and find a place to spawn it
                LevelObjectPrefab prefab = GetRandomPrefab(level.GenerationParams.Name);

                SpawnPosition spawnPosition = FindObjectPosition(availableSpawnPositions, level, prefab);

                if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None)
                {
                    continue;
                }

                float rotation = 0.0f;
                if (prefab.AlignWithSurface && spawnPosition != null)
                {
                    rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X));
                }
                rotation += Rand.Range(prefab.RandomRotationRad.X, prefab.RandomRotationRad.Y, Rand.RandSync.Server);

                Vector2 position = Vector2.Zero;
                Vector2 edgeDir  = Vector2.UnitX;
                if (spawnPosition == null)
                {
                    position = new Vector2(
                        Rand.Range(0.0f, level.Size.X, Rand.RandSync.Server),
                        Rand.Range(0.0f, level.Size.Y, Rand.RandSync.Server));
                }
                else
                {
                    edgeDir  = (spawnPosition.GraphEdge.Point1 - spawnPosition.GraphEdge.Point2) / spawnPosition.Length;
                    position = spawnPosition.GraphEdge.Point2 + edgeDir * Rand.Range(prefab.MinSurfaceWidth / 2.0f, spawnPosition.Length - prefab.MinSurfaceWidth / 2.0f, Rand.RandSync.Server);
                }

                var newObject = new LevelObject(prefab,
                                                new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);
                AddObject(newObject, level);

                foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
                {
                    int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
                    for (int j = 0; j < childCount; j++)
                    {
                        var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
                        int prefabCount     = matchingPrefabs.Count();
                        var childPrefab     = prefabCount == 0 ? null : matchingPrefabs.ElementAt(Rand.Range(0, prefabCount, Rand.RandSync.Server));
                        if (childPrefab == null)
                        {
                            continue;
                        }

                        Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;

                        var childObject = new LevelObject(childPrefab,
                                                          new Vector3(childPos, Rand.Range(childPrefab.DepthRange.X, childPrefab.DepthRange.Y, Rand.RandSync.Server)),
                                                          Rand.Range(childPrefab.MinSize, childPrefab.MaxSize, Rand.RandSync.Server),
                                                          rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));

                        AddObject(childObject, level);
                    }
                }
            }
        }
        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 (RuinGeneration.Ruin ruin in level.Ruins)
            {
                foreach (var ruinShape in ruin.RuinShapes)
                {
                    foreach (var wall in ruinShape.Walls)
                    {
                        availableSpawnPositions.Add(new SpawnPosition(
                                                        new GraphEdge(wall.A, wall.B),
                                                        (wall.A + wall.B) / 2.0f - ruinShape.Center,
                                                        LevelObjectPrefab.SpawnPosType.RuinWall,
                                                        ruinShape.GetLineAlignment(wall)));
                    }
                }
            }

            foreach (var posOfInterest in level.PositionsOfInterest)
            {
                if (posOfInterest.PositionType != Level.PositionType.MainPath)
                {
                    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>();

            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.Identifier, availablePrefabs);
                if (prefab == null)
                {
                    continue;
                }
                if (!suitableSpawnPositions.ContainsKey(prefab))
                {
                    suitableSpawnPositions.Add(prefab, availableSpawnPositions.Where(sp =>
                                                                                     prefab.SpawnPos.HasFlag(sp.SpawnPosType) && (sp.Length >= prefab.MinSurfaceWidth && prefab.Alignment.HasFlag(sp.Alignment) || sp.SpawnPosType == LevelObjectPrefab.SpawnPosType.LevelEnd || sp.SpawnPosType == LevelObjectPrefab.SpawnPosType.LevelStart)).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;
                }

                float rotation = 0.0f;
                if (prefab.AlignWithSurface && spawnPosition != null)
                {
                    rotation = MathUtils.VectorToAngle(new Vector2(spawnPosition.Normal.Y, spawnPosition.Normal.X));
                }
                rotation += Rand.Range(prefab.RandomRotationRad.X, prefab.RandomRotationRad.Y, Rand.RandSync.Server);

                Vector2 position = Vector2.Zero;
                Vector2 edgeDir  = Vector2.UnitX;
                if (spawnPosition == null)
                {
                    position = new Vector2(
                        Rand.Range(0.0f, level.Size.X, Rand.RandSync.Server),
                        Rand.Range(0.0f, level.Size.Y, Rand.RandSync.Server));
                }
                else
                {
                    edgeDir  = (spawnPosition.GraphEdge.Point1 - spawnPosition.GraphEdge.Point2) / spawnPosition.Length;
                    position = spawnPosition.GraphEdge.Point2 + edgeDir * Rand.Range(prefab.MinSurfaceWidth / 2.0f, spawnPosition.Length - prefab.MinSurfaceWidth / 2.0f, Rand.RandSync.Server);
                }

                var newObject = new LevelObject(prefab,
                                                new Vector3(position, Rand.Range(prefab.DepthRange.X, prefab.DepthRange.Y, Rand.RandSync.Server)), Rand.Range(prefab.MinSize, prefab.MaxSize, Rand.RandSync.Server), rotation);
                AddObject(newObject, level);
                if (prefab.MaxCount < amount)
                {
                    if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
                    {
                        availablePrefabs.Remove(prefab);
                    }
                }

                foreach (LevelObjectPrefab.ChildObject child in prefab.ChildObjects)
                {
                    int childCount = Rand.Range(child.MinCount, child.MaxCount, Rand.RandSync.Server);
                    for (int j = 0; j < childCount; j++)
                    {
                        var matchingPrefabs = LevelObjectPrefab.List.Where(p => child.AllowedNames.Contains(p.Name));
                        int prefabCount     = matchingPrefabs.Count();
                        var childPrefab     = prefabCount == 0 ? null : matchingPrefabs.ElementAt(Rand.Range(0, prefabCount, Rand.RandSync.Server));
                        if (childPrefab == null)
                        {
                            continue;
                        }

                        Vector2 childPos = position + edgeDir * Rand.Range(-0.5f, 0.5f, Rand.RandSync.Server) * prefab.MinSurfaceWidth;

                        var childObject = new LevelObject(childPrefab,
                                                          new Vector3(childPos, Rand.Range(childPrefab.DepthRange.X, childPrefab.DepthRange.Y, Rand.RandSync.Server)),
                                                          Rand.Range(childPrefab.MinSize, childPrefab.MaxSize, Rand.RandSync.Server),
                                                          rotation + Rand.Range(childPrefab.RandomRotationRad.X, childPrefab.RandomRotationRad.Y, Rand.RandSync.Server));

                        AddObject(childObject, level);
                    }
                }
            }
        }
        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 (RuinGeneration.Ruin ruin in level.Ruins)
            {
                foreach (var ruinShape in ruin.RuinShapes)
                {
                    foreach (var wall in ruinShape.Walls)
                    {
                        availableSpawnPositions.Add(new SpawnPosition(
                                                        new GraphEdge(wall.A, wall.B),
                                                        (wall.A + wall.B) / 2.0f - ruinShape.Center,
                                                        LevelObjectPrefab.SpawnPosType.RuinWall,
                                                        ruinShape.GetLineAlignment(wall)));
                    }
                }
            }

            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))
                {
                    suitableSpawnPositions.Add(prefab,
                                               availableSpawnPositions.Where(sp =>
                                                                             sp.SpawnPosTypes.Any(type => prefab.SpawnPos.HasFlag(type)) &&
                                                                             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 (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);
                    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 (prefab.MaxCount < amount)
                    {
                        if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount)
                        {
                            availablePrefabs.Remove(prefab);
                        }
                    }
                }
            }
        }
        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);
                        }
                    }
                }
            }
        }