private void OnObjectTriggered(LevelObject triggeredObject, LevelTrigger trigger, Entity triggerer)
 {
     if (trigger.TriggerOthersDistance <= 0.0f)
     {
         return;
     }
     foreach (LevelObject obj in objects)
     {
         if (obj == triggeredObject)
         {
             continue;
         }
         foreach (LevelTrigger otherTrigger in obj.Triggers)
         {
             otherTrigger.OtherTriggered(triggeredObject, trigger);
         }
     }
 }
        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);
                    }
                }
            }
        }
        private void AddObject(LevelObject newObject, Level level)
        {
            foreach (LevelTrigger trigger in newObject.Triggers)
            {
                trigger.OnTriggered += (levelTrigger, obj) =>
                {
                    OnObjectTriggered(newObject, levelTrigger, obj);
                };
            }

            var spriteCorners = new List <Vector2>
            {
                Vector2.Zero, Vector2.Zero, Vector2.Zero, Vector2.Zero
            };

            Sprite sprite = newObject.Sprite ?? newObject.Prefab.DeformableSprite?.Sprite;

            //calculate the positions of the corners of the rotated sprite
            if (sprite != null)
            {
                Vector2 halfSize = sprite.size * newObject.Scale / 2;
                spriteCorners[0] = -halfSize;
                spriteCorners[1] = new Vector2(-halfSize.X, halfSize.Y);
                spriteCorners[2] = halfSize;
                spriteCorners[3] = new Vector2(halfSize.X, -halfSize.Y);

                Vector2 pivotOffset = sprite.Origin * newObject.Scale - halfSize;
                pivotOffset.X = -pivotOffset.X;
                pivotOffset   = new Vector2(
                    (float)(pivotOffset.X * Math.Cos(-newObject.Rotation) - pivotOffset.Y * Math.Sin(-newObject.Rotation)),
                    (float)(pivotOffset.X * Math.Sin(-newObject.Rotation) + pivotOffset.Y * Math.Cos(-newObject.Rotation)));

                for (int j = 0; j < 4; j++)
                {
                    spriteCorners[j] = new Vector2(
                        (float)(spriteCorners[j].X * Math.Cos(-newObject.Rotation) - spriteCorners[j].Y * Math.Sin(-newObject.Rotation)),
                        (float)(spriteCorners[j].X * Math.Sin(-newObject.Rotation) + spriteCorners[j].Y * Math.Cos(-newObject.Rotation)));

                    spriteCorners[j] += new Vector2(newObject.Position.X, newObject.Position.Y) + pivotOffset;
                }
            }

            float minX = spriteCorners.Min(c => c.X) - newObject.Position.Z;
            float maxX = spriteCorners.Max(c => c.X) + newObject.Position.Z;

            float minY = spriteCorners.Min(c => c.Y) - newObject.Position.Z - level.BottomPos;
            float maxY = spriteCorners.Max(c => c.Y) + newObject.Position.Z - level.BottomPos;

            foreach (LevelTrigger trigger in newObject.Triggers)
            {
                if (trigger.PhysicsBody == null)
                {
                    continue;
                }
                for (int i = 0; i < trigger.PhysicsBody.FarseerBody.FixtureList.Count; i++)
                {
                    trigger.PhysicsBody.FarseerBody.GetTransform(out FarseerPhysics.Common.Transform transform);
                    trigger.PhysicsBody.FarseerBody.FixtureList[i].Shape.ComputeAABB(out FarseerPhysics.Collision.AABB aabb, ref transform, i);

                    minX = Math.Min(minX, ConvertUnits.ToDisplayUnits(aabb.LowerBound.X));
                    maxX = Math.Max(maxX, ConvertUnits.ToDisplayUnits(aabb.UpperBound.X));
                    minY = Math.Min(minY, ConvertUnits.ToDisplayUnits(aabb.LowerBound.Y) - level.BottomPos);
                    maxY = Math.Max(maxY, ConvertUnits.ToDisplayUnits(aabb.UpperBound.Y) - level.BottomPos);
                }
            }


#if CLIENT
            if (newObject.ParticleEmitters != null)
            {
                foreach (ParticleEmitter emitter in newObject.ParticleEmitters)
                {
                    Rectangle particleBounds = emitter.CalculateParticleBounds(new Vector2(newObject.Position.X, newObject.Position.Y));
                    minX = Math.Min(minX, particleBounds.X);
                    maxX = Math.Max(maxX, particleBounds.Right);
                    minY = Math.Min(minY, particleBounds.Y - level.BottomPos);
                    maxY = Math.Max(maxY, particleBounds.Bottom - level.BottomPos);
                }
            }
#endif
            objects.Add(newObject);
            newObject.Position.Z += (minX + minY) % 100.0f * 0.00001f;

            int xStart = (int)Math.Floor(minX / GridSize);
            int xEnd   = (int)Math.Floor(maxX / GridSize);
            if (xEnd < 0 || xStart >= objectGrid.GetLength(0))
            {
                return;
            }

            int yStart = (int)Math.Floor(minY / GridSize);
            int yEnd   = (int)Math.Floor(maxY / GridSize);
            if (yEnd < 0 || yStart >= objectGrid.GetLength(1))
            {
                return;
            }

            xStart = Math.Max(xStart, 0);
            xEnd   = Math.Min(xEnd, objectGrid.GetLength(0) - 1);
            yStart = Math.Max(yStart, 0);
            yEnd   = Math.Min(yEnd, objectGrid.GetLength(1) - 1);

            for (int x = xStart; x <= xEnd; x++)
            {
                for (int y = yStart; y <= yEnd; y++)
                {
                    if (objectGrid[x, y] == null)
                    {
                        objectGrid[x, y] = new List <LevelObject>();
                    }
                    objectGrid[x, y].Add(newObject);
                }
            }
        }
        private void PlaceObject(LevelObjectPrefab prefab, SpawnPosition spawnPosition, Level level, Level.Cave parentCave = null)
        {
            float rotation = 0.0f;

            if (prefab.AlignWithSurface && spawnPosition.Normal.LengthSquared() > 0.001f && 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);
            }

            if (!MathUtils.NearlyEqual(prefab.RandomOffset.X, 0.0f) || !MathUtils.NearlyEqual(prefab.RandomOffset.Y, 0.0f))
            {
                Vector2 offsetDir = spawnPosition.Normal.LengthSquared() > 0.001f ? spawnPosition.Normal : Rand.Vector(1.0f, Rand.RandSync.Server);
                position += offsetDir * Rand.Range(prefab.RandomOffset.X, prefab.RandomOffset.Y, 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);
            newObject.ParentCave = parentCave;

            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);
                    childObject.ParentCave = parentCave;
                }
            }
        }