示例#1
0
        protected virtual Vector2 DoSteeringWander(float weight)
        {
            Vector2 circleCenter = (host.Steering == Vector2.Zero) ? Rand.Vector(weight) : host.Steering;

            circleCenter = Vector2.Normalize(circleCenter) * CircleDistance;

            Vector2 displacement = new Vector2(
                (float)Math.Cos(wanderAngle),
                (float)Math.Sin(wanderAngle));

            displacement = displacement * CircleRadius;

            float angleChange = 1.5f;

            wanderAngle += Rand.Range(0.0f, 1.0f) * angleChange - angleChange * 0.5f;

            Vector2 newSteering   = circleCenter + displacement;
            float   steeringSpeed = (newSteering + host.Steering).Length();

            if (steeringSpeed > weight)
            {
                newSteering = Vector2.Normalize(newSteering) * weight;
            }

            return(newSteering);
        }
示例#2
0
 private void InitializeMonsters(IEnumerable <Character> monsters)
 {
     foreach (var monster in monsters)
     {
         monster.Enabled = false;
         if (monster.Params.AI.EnforceAggressiveBehaviorForMissions)
         {
             foreach (var targetParam in monster.Params.AI.Targets)
             {
                 switch (targetParam.State)
                 {
                 case AIState.Avoid:
                 case AIState.Escape:
                 case AIState.Flee:
                 case AIState.PassiveAggressive:
                     targetParam.State = AIState.Attack;
                     break;
                 }
             }
         }
     }
     SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>());
     foreach (Character monster in monsters)
     {
         tempSonarPositions.Add(monster.WorldPosition + Rand.Vector(maxSonarMarkerDistance));
     }
     if (monsters.Count() != tempSonarPositions.Count)
     {
         throw new Exception($"monsters.Count != tempSonarPositions.Count ({monsters.Count()} != {tempSonarPositions.Count})");
     }
 }
        private void HandleLevelCollision(Impact impact)
        {
            if (GameMain.GameSession != null && Timing.TotalTime < GameMain.GameSession.RoundStartTime + 10)
            {
                //ignore level collisions for the first 10 seconds of the round in case the sub spawns in a way that causes it to hit a wall
                //(e.g. level without outposts to dock to and an incorrectly configured ballast that makes the sub go up)
                return;
            }

            float wallImpact = Vector2.Dot(impact.Velocity, -impact.Normal);

            ApplyImpact(wallImpact, -impact.Normal, impact.ImpactPos);
            foreach (Submarine dockedSub in submarine.DockedTo)
            {
                dockedSub.SubBody.ApplyImpact(wallImpact, -impact.Normal, impact.ImpactPos);
            }

#if CLIENT
            int particleAmount = (int)Math.Min(wallImpact * 10.0f, 50);
            for (int i = 0; i < particleAmount; i++)
            {
                GameMain.ParticleManager.CreateParticle("iceshards",
                                                        ConvertUnits.ToDisplayUnits(impact.ImpactPos) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
                                                        Rand.Vector(Rand.Range(50.0f, 500.0f)) + impact.Velocity);
            }
#endif
        }
示例#4
0
        partial void ImplodeFX()
        {
            Vector2 centerOfMass = AnimController.GetCenterOfMass();

            SoundPlayer.PlaySound("implode", 1.0f, 150.0f, WorldPosition);

            for (int i = 0; i < 10; i++)
            {
                Particle p = GameMain.ParticleManager.CreateParticle("waterblood",
                                                                     ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
                                                                     Rand.Vector(10.0f));
                if (p != null)
                {
                    p.Size *= 2.0f;
                }

                GameMain.ParticleManager.CreateParticle("bubbles",
                                                        ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
                                                        new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f, 50f)));

                GameMain.ParticleManager.CreateParticle("gib",
                                                        WorldPosition + Rand.Vector(Rand.Range(0.0f, 50.0f)),
                                                        Rand.Range(0.0f, MathHelper.TwoPi),
                                                        Rand.Range(200.0f, 700.0f), null);
            }

            for (int i = 0; i < 30; i++)
            {
                GameMain.ParticleManager.CreateParticle("heavygib",
                                                        WorldPosition + Rand.Vector(Rand.Range(0.0f, 50.0f)),
                                                        Rand.Range(0.0f, MathHelper.TwoPi),
                                                        Rand.Range(50.0f, 500.0f), null);
            }
        }
示例#5
0
            public MapTile(Sprite sprite, SpriteEffects spriteEffect)
            {
                Sprite       = sprite;
                SpriteEffect = spriteEffect;

                Offset = Rand.Vector(Rand.Range(0.0f, 1.0f));
            }
        protected virtual Vector2 DoSteeringAvoid(float deltaTime, float speed = 1.0f)
        {
            if (steering == Vector2.Zero || host.Steering == Vector2.Zero)
            {
                return(Vector2.Zero);
            }

            float maxDistance = 2.0f;

            Vector2 ahead = host.SimPosition + Vector2.Normalize(host.Steering) * maxDistance;

            if (rayCastTimer <= 0.0f)
            {
                rayCastTimer = RayCastInterval;
                Body closestBody = Submarine.CheckVisibility(host.SimPosition, ahead);
                if (closestBody == null)
                {
                    avoidSteering = Vector2.Zero;
                    return(Vector2.Zero);
                }
                else
                {
                    if (closestBody.UserData is Structure closestStructure)
                    {
                        Vector2 obstaclePosition = Submarine.LastPickedPosition;
                        if (closestStructure.IsHorizontal)
                        {
                            obstaclePosition.Y = closestStructure.SimPosition.Y;
                        }
                        else
                        {
                            obstaclePosition.X = closestStructure.SimPosition.X;
                        }

                        avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition);
                    }
                    else if (closestBody.UserData is Item item)
                    {
                        avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition);
                    }
                    else
                    {
                        avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition);
                    }
                    //failed to normalize (the obstacle to avoid is at the same position as the character?)
                    // -> move to a random direction
                    if (!MathUtils.IsValid(avoidSteering))
                    {
                        avoidSteering = Rand.Vector(1.0f);
                    }
                }
            }
            else
            {
                rayCastTimer -= deltaTime;
            }

            return(avoidSteering * speed);
        }
示例#7
0
        partial void UpdateProjSpecific(float deltaTime)
        {
            if (!body.Enabled)
            {
                return;
            }

            if (!character.IsDead)
            {
                DamageOverlayStrength -= deltaTime;
                BurnOverlayStrength   -= deltaTime;
            }
            else
            {
                var spriteParams = Params.GetSprite();
                if (spriteParams.DeadColorTime > 0 && deadTimer < spriteParams.DeadColorTime)
                {
                    deadTimer += deltaTime;
                }
            }

            if (inWater)
            {
                wetTimer = 1.0f;
            }
            else
            {
                wetTimer -= deltaTime * 0.1f;
                if (wetTimer > 0.0f)
                {
                    dripParticleTimer += wetTimer * deltaTime * Mass * (wetTimer > 0.9f ? 50.0f : 5.0f);
                    if (dripParticleTimer > 1.0f)
                    {
                        float dropRadius = body.BodyShape == PhysicsBody.Shape.Rectangle ? Math.Min(body.width, body.height) : body.radius;
                        GameMain.ParticleManager.CreateParticle(
                            "waterdrop",
                            WorldPosition + Rand.Vector(Rand.Range(0.0f, ConvertUnits.ToDisplayUnits(dropRadius))),
                            ConvertUnits.ToDisplayUnits(body.LinearVelocity),
                            0, character.CurrentHull);
                        dripParticleTimer = 0.0f;
                    }
                }
            }

            if (LightSource != null)
            {
                LightSource.ParentSub = body.Submarine;
                LightSource.Rotation  = (dir == Direction.Right) ? body.Rotation : body.Rotation - MathHelper.Pi;
                if (LightSource.LightSprite != null)
                {
                    LightSource.LightSprite.Depth = ActiveSprite.Depth;
                }
            }

            UpdateSpriteStates(deltaTime);
        }
示例#8
0
        protected override void Act(float deltaTime)
        {
            coolDownTimer -= deltaTime;

            var weapon = character.Inventory.FindItem("weapon");

            if (weapon == null)
            {
                Escape(deltaTime);
            }
            else
            {
                //TODO: make sure the weapon is ready to use (projectiles/batteries loaded)
                if (!character.SelectedItems.Contains(weapon))
                {
                    if (character.Inventory.TryPutItem(weapon, 3, false, false, character))
                    {
                        weapon.Equip(character);
                    }
                    else
                    {
                        return;
                    }
                }
                character.CursorPosition = enemy.Position;
                character.SetInput(InputType.Aim, false, true);

                Vector2 enemyDiff = Vector2.Normalize(enemy.Position - character.Position);
                if (!MathUtils.IsValid(enemyDiff))
                {
                    enemyDiff = Rand.Vector(1.0f);
                }
                float   weaponAngle = ((weapon.body.Dir == 1.0f) ? weapon.body.Rotation : weapon.body.Rotation - MathHelper.Pi);
                Vector2 weaponDir   = new Vector2((float)Math.Cos(weaponAngle), (float)Math.Sin(weaponAngle));

                if (Vector2.Dot(enemyDiff, weaponDir) < 0.9f)
                {
                    return;
                }

                List <FarseerPhysics.Dynamics.Body> ignoredBodies = new List <FarseerPhysics.Dynamics.Body>();
                foreach (Limb limb in character.AnimController.Limbs)
                {
                    ignoredBodies.Add(limb.body.FarseerBody);
                }

                var pickedBody = Submarine.PickBody(character.SimPosition, enemy.SimPosition, ignoredBodies);
                if (pickedBody != null && !(pickedBody.UserData is Limb))
                {
                    return;
                }

                weapon.Use(deltaTime, character);
            }
        }
        public void SpawnSprites(int count, Vector2?position = null)
        {
            activeSprites.Clear();

            if (prefabs.Count == 0)
            {
                return;
            }

            count = Math.Min(count, MaxSprites);

            for (int i = 0; i < count; i++)
            {
                Vector2 pos = Vector2.Zero;

                if (position == null)
                {
                    var wayPoints = WayPoint.WayPointList.FindAll(wp => wp.Submarine == null);
                    if (wayPoints.Any())
                    {
                        WayPoint wp = wayPoints[Rand.Int(wayPoints.Count, Rand.RandSync.ClientOnly)];

                        pos  = new Vector2(wp.Rect.X, wp.Rect.Y);
                        pos += Rand.Vector(200.0f, Rand.RandSync.ClientOnly);
                    }
                    else
                    {
                        pos = Rand.Vector(2000.0f, Rand.RandSync.ClientOnly);
                    }
                }
                else
                {
                    pos = (Vector2)position;
                }


                var prefab = prefabs[Rand.Int(prefabs.Count, Rand.RandSync.ClientOnly)];

                int amount = Rand.Range(prefab.SwarmMin, prefab.SwarmMax, Rand.RandSync.ClientOnly);
                List <BackgroundCreature> swarmMembers = new List <BackgroundCreature>();

                for (int n = 0; n < amount; n++)
                {
                    var newSprite = new BackgroundCreature(prefab, pos);
                    activeSprites.Add(newSprite);
                    swarmMembers.Add(newSprite);
                }
                if (amount > 0)
                {
                    new Swarm(swarmMembers, prefab.SwarmRadius);
                }
            }
        }
示例#10
0
        partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull)
        {
            if (shockwave)
            {
                GameMain.ParticleManager.CreateParticle("shockwave", worldPosition,
                                                        Vector2.Zero, 0.0f, hull);
            }

            for (int i = 0; i < attack.Range * 0.1f; i++)
            {
                Vector2 bubblePos = Rand.Vector(attack.Range * 0.5f);
                GameMain.ParticleManager.CreateParticle("bubbles", worldPosition + bubblePos,
                                                        bubblePos, 0.0f, hull);

                if (sparks)
                {
                    GameMain.ParticleManager.CreateParticle("spark", worldPosition,
                                                            Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f, hull);
                }
                if (flames)
                {
                    GameMain.ParticleManager.CreateParticle("explosionfire", ClampParticlePos(worldPosition + Rand.Vector(50f), hull),
                                                            Rand.Vector(Rand.Range(50.0f, 100.0f)), 0.0f, hull);
                }
                if (smoke)
                {
                    GameMain.ParticleManager.CreateParticle("smoke", ClampParticlePos(worldPosition + Rand.Vector(50f), hull),
                                                            Rand.Vector(Rand.Range(1.0f, 10.0f)), 0.0f, hull);
                }
            }

            if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
            {
                hull.AddDecal(decal, worldPosition, decalSize);
            }

            if (flash)
            {
                float displayRange = attack.Range;
                if (displayRange < 0.1f)
                {
                    return;
                }

                var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);
                CoroutineManager.StartCoroutine(DimLight(light));
            }
        }
示例#11
0
        partial void AddDamageProjSpecific(float damage, Vector2 worldPosition)
        {
            if (damage <= 0.0f)
            {
                return;
            }
            Vector2 particlePos = worldPosition;

            if (!Cells.Any(c => c.IsPointInside(particlePos)))
            {
                bool intersectionFound = false;
                foreach (var cell in Cells)
                {
                    foreach (var edge in cell.Edges)
                    {
                        if (MathUtils.GetLineIntersection(worldPosition, cell.Center, edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, out Vector2 intersection))
                        {
                            intersectionFound = true;
                            particlePos       = intersection;
                            break;
                        }
                    }
                    if (intersectionFound)
                    {
                        break;
                    }
                }
            }

            Vector2 particleDir = particlePos - WorldPosition;

            if (particleDir.LengthSquared() > 0.0001f)
            {
                particleDir = Vector2.Normalize(particleDir);
            }
            int particleAmount = MathHelper.Clamp((int)damage, 1, 10);

            for (int i = 0; i < particleAmount; i++)
            {
                var particle = GameMain.ParticleManager.CreateParticle("iceshards",
                                                                       particlePos + Rand.Vector(5.0f),
                                                                       particleDir * Rand.Range(200.0f, 500.0f) + Rand.Vector(100.0f));
            }
        }
示例#12
0
        private void HandleLevelCollision(Impact impact, VoronoiCell cell = null)
        {
            if (GameMain.GameSession != null && Timing.TotalTime < GameMain.GameSession.RoundStartTime + 10)
            {
                //ignore level collisions for the first 10 seconds of the round in case the sub spawns in a way that causes it to hit a wall
                //(e.g. level without outposts to dock to and an incorrectly configured ballast that makes the sub go up)
                return;
            }

            float wallImpact = Vector2.Dot(impact.Velocity, -impact.Normal);

            ApplyImpact(wallImpact, -impact.Normal, impact.ImpactPos);
            foreach (Submarine dockedSub in submarine.DockedTo)
            {
                dockedSub.SubBody.ApplyImpact(wallImpact, -impact.Normal, impact.ImpactPos);
            }

            if (cell != null && cell.IsDestructible && wallImpact > 0.0f)
            {
                var hitWall = Level.Loaded?.ExtraWalls.Find(w => w.Cells.Contains(cell));
                if (hitWall != null && hitWall.WallDamageOnTouch > 0.0f)
                {
                    var damagedStructures = Explosion.RangedStructureDamage(
                        ConvertUnits.ToDisplayUnits(impact.ImpactPos),
                        500.0f,
                        hitWall.WallDamageOnTouch,
                        levelWallDamage: 0.0f);
#if CLIENT
                    PlayDamageSounds(damagedStructures, impact.ImpactPos, wallImpact, "StructureSlash");
#endif
                }
            }

#if CLIENT
            int particleAmount = (int)Math.Min(wallImpact * 10.0f, 50);
            for (int i = 0; i < particleAmount; i++)
            {
                GameMain.ParticleManager.CreateParticle("iceshards",
                                                        ConvertUnits.ToDisplayUnits(impact.ImpactPos) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
                                                        Rand.Vector(Rand.Range(50.0f, 500.0f)) + impact.Velocity);
            }
#endif
        }
示例#13
0
        public bool OnCollision(Fixture f1, Fixture f2, Contact contact)
        {
            if (f2.Body.UserData is Limb limb)
            {
                bool collision = CheckLimbCollision(contact, limb);
                if (collision)
                {
                    HandleLimbCollision(contact, limb);
                }
                return(collision);
            }

            if (f2.UserData is VoronoiCell cell)
            {
                Vector2 collisionNormal = Vector2.Normalize(ConvertUnits.ToDisplayUnits(Body.SimPosition) - cell.Center);
                if (!MathUtils.IsValid(collisionNormal))
                {
                    collisionNormal = Rand.Vector(1.0f);
                }
                HandleLevelCollision(contact, collisionNormal);
                return(true);
            }

            if (f2.Body.UserData is Structure structure)
            {
                contact.GetWorldManifold(out Vector2 normal, out FixedArray2 <Vector2> points);
                if (contact.FixtureA.Body == f1.Body)
                {
                    normal = -normal;
                }

                HandleLevelCollision(contact, normal);
                return(true);
            }

            if (f2.Body.UserData is Submarine otherSub)
            {
                HandleSubCollision(contact, otherSub);
                return(true);
            }

            return(true);
        }
示例#14
0
        public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true)
        {
            prevPosition = position;
            prevZoom     = zoom;

            float moveSpeed = 20.0f / zoom;

            Vector2 moveCam = Vector2.Zero;

            if (targetPos == Vector2.Zero)
            {
            }
            else
            {
                Vector2 mousePos = PlayerInput.MousePosition;

                Vector2 offset = mousePos - new Vector2(resolution.X / 2.0f, resolution.Y / 2.0f);

                offset.X = offset.X / (resolution.X * 0.4f);
                offset.Y = -offset.Y / (resolution.Y * 0.3f);

                if (offset.Length() > 1.0f)
                {
                    offset.Normalize();
                }

                offset = offset * offsetAmount;

                float newZoom = Math.Min(DefaultZoom - Math.Min(offset.Length() / resolution.Y, 1.0f), 1.0f);
                Zoom += (newZoom - zoom) / ZoomSmoothness;

                Vector2 diff = (targetPos + offset) - position;

                moveCam = diff / MoveSmoothness;
            }

            shakeTargetPosition = Rand.Vector(Shake);
            shakePosition       = Vector2.Lerp(shakePosition, shakeTargetPosition, 0.5f);
            Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);

            Translate(moveCam + shakePosition);
        }
示例#15
0
        private void HandleLevelCollision(Impact impact)
        {
            float wallImpact = Vector2.Dot(impact.Velocity, -impact.Normal);

            ApplyImpact(wallImpact, -impact.Normal, impact.ImpactPos);
            foreach (Submarine dockedSub in submarine.DockedTo)
            {
                dockedSub.SubBody.ApplyImpact(wallImpact, -impact.Normal, impact.ImpactPos);
            }

#if CLIENT
            int particleAmount = (int)Math.Min(wallImpact * 10.0f, 50);
            for (int i = 0; i < particleAmount; i++)
            {
                GameMain.ParticleManager.CreateParticle("iceshards",
                                                        ConvertUnits.ToDisplayUnits(impact.ImpactPos) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
                                                        Rand.Vector(Rand.Range(50.0f, 500.0f)) + impact.Velocity);
            }
#endif
        }
示例#16
0
        public Vector2 GetRandomItemPos(PositionType spawnPosType, float randomSpread, float minDistFromSubs, float offsetFromWall = 10.0f)
        {
            if (!positionsOfInterest.Any())
            {
                return(Size * 0.5f);
            }

            Vector2 position = Vector2.Zero;

            offsetFromWall = ConvertUnits.ToSimUnits(offsetFromWall);

            int tries = 0;

            do
            {
                Vector2 startPos;
                Loaded.TryGetInterestingPosition(true, spawnPosType, minDistFromSubs, out startPos);

                startPos += Rand.Vector(Rand.Range(0.0f, randomSpread, Rand.RandSync.Server), Rand.RandSync.Server);

                Vector2 endPos = startPos - Vector2.UnitY * Size.Y;

                if (Submarine.PickBody(
                        ConvertUnits.ToSimUnits(startPos),
                        ConvertUnits.ToSimUnits(endPos),
                        null, Physics.CollisionLevel) != null)
                {
                    position = ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.Normalize(startPos - endPos) * offsetFromWall;
                    break;
                }

                tries++;

                if (tries == 10)
                {
                    position = EndPosition - Vector2.UnitY * 300.0f;
                }
            } while (tries < 10);

            return(position);
        }
示例#17
0
        private void HandleLevelCollision(Contact contact, Vector2 collisionNormal)
        {
            float wallImpact = Vector2.Dot(Velocity, -collisionNormal);

            ApplyImpact(wallImpact, -collisionNormal, contact);
            foreach (Submarine dockedSub in submarine.DockedTo)
            {
                dockedSub.SubBody.ApplyImpact(wallImpact, -collisionNormal, contact);
            }

#if CLIENT
            contact.GetWorldManifold(out _, out FixedArray2 <Vector2> particlePos);

            int particleAmount = (int)Math.Min(wallImpact * 10.0f, 50);
            for (int i = 0; i < particleAmount; i++)
            {
                GameMain.ParticleManager.CreateParticle("iceshards",
                                                        ConvertUnits.ToDisplayUnits(particlePos[0]) + Rand.Vector(Rand.Range(1.0f, 50.0f)),
                                                        Rand.Vector(Rand.Range(50.0f, 500.0f)) + Velocity);
            }
#endif
        }
示例#18
0
        public override void Update(float deltaTime)
        {
            if (disallowed)
            {
                Finished();
                return;
            }

            if (isFinished)
            {
                return;
            }

            if (spawnPos == null)
            {
                FindSpawnPosition(affectSubImmediately: true);
                spawnPending = true;
            }

            bool spawnReady = false;

            if (spawnPending)
            {
                //if spawning in a ruin/cave, wait for someone to be close to it to spawning
                //unnecessary monsters in places the players might never visit during the round
                if (spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Cave || spawnPosType == Level.PositionType.Wreck)
                {
                    bool  someoneNearby = false;
                    float minDist       = Sonar.DefaultSonarRange * 0.8f;
                    foreach (Submarine submarine in Submarine.Loaded)
                    {
                        if (submarine.Info.Type != SubmarineInfo.SubmarineType.Player)
                        {
                            continue;
                        }
                        if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist)
                        {
                            someoneNearby = true;
                            break;
                        }
                    }
                    foreach (Character c in Character.CharacterList)
                    {
                        if (c == Character.Controlled || c.IsRemotePlayer)
                        {
                            if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < minDist * minDist)
                            {
                                someoneNearby = true;
                                break;
                            }
                        }
                    }
                    if (!someoneNearby)
                    {
                        return;
                    }
                }
                else
                {
                    //wait until there are no submarines at the spawnpos
                    foreach (Submarine submarine in Submarine.Loaded)
                    {
                        if (submarine.Info.Type != SubmarineInfo.SubmarineType.Player)
                        {
                            continue;
                        }
                        float minDist = GetMinDistanceToSub(submarine);
                        if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist)
                        {
                            return;
                        }
                    }
                }

                spawnPending = false;

                //+1 because Range returns an integer less than the max value
                int amount = Rand.Range(minAmount, maxAmount + 1);
                monsters = new List <Character>();
                float offsetAmount = spawnPosType == Level.PositionType.MainPath ? scatter : 100;
                for (int i = 0; i < amount; i++)
                {
                    CoroutineManager.InvokeAfter(() =>
                    {
                        //round ended before the coroutine finished
                        if (GameMain.GameSession == null || Level.Loaded == null)
                        {
                            return;
                        }

                        System.Diagnostics.Debug.Assert(GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer, "Clients should not create monster events.");

                        Vector2 pos = spawnPos.Value + Rand.Vector(offsetAmount);
                        if (spawnPosType == Level.PositionType.MainPath)
                        {
                            if (Submarine.Loaded.Any(s => ToolBox.GetWorldBounds(s.Borders.Center, s.Borders.Size).ContainsWorld(pos)))
                            {
                                // Can't use the offset position, let's use the exact spawn position.
                                pos = spawnPos.Value;
                            }
                            else if (Level.Loaded.Ruins.Any(r => ToolBox.GetWorldBounds(r.Area.Center, r.Area.Size).ContainsWorld(pos)))
                            {
                                // Can't use the offset position, let's use the exact spawn position.
                                pos = spawnPos.Value;
                            }
                        }

                        monsters.Add(Character.Create(speciesName, pos, Level.Loaded.Seed + i.ToString(), null, false, true, true));

                        if (monsters.Count == amount)
                        {
                            spawnReady = true;
                            //this will do nothing if the monsters have no swarm behavior defined,
                            //otherwise it'll make the spawned characters act as a swarm
                            SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>());
                        }
                    }, Rand.Range(0f, amount / 2));
                }
            }

            if (!spawnReady)
            {
                return;
            }

            Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter);

#if CLIENT
            if (Character.Controlled != null)
            {
                targetEntity = Character.Controlled;
            }
#endif

            bool monstersDead = true;
            foreach (Character monster in monsters)
            {
                if (!monster.IsDead)
                {
                    monstersDead = false;

                    if (targetEntity != null && Vector2.DistanceSquared(monster.WorldPosition, targetEntity.WorldPosition) < 5000.0f * 5000.0f)
                    {
                        break;
                    }
                }
            }

            if (monstersDead)
            {
                Finished();
            }
        }
        public void Explode(Vector2 worldPosition)
        {
            Hull hull = Hull.FindHull(worldPosition);

            if (shockwave)
            {
                GameMain.ParticleManager.CreateParticle("shockwave", worldPosition,
                                                        Vector2.Zero, 0.0f, hull);
            }

            for (int i = 0; i < attack.Range * 0.1f; i++)
            {
                Vector2 bubblePos = Rand.Vector(attack.Range * 0.5f);
                GameMain.ParticleManager.CreateParticle("bubbles", worldPosition + bubblePos,
                                                        bubblePos, 0.0f, hull);

                if (sparks)
                {
                    GameMain.ParticleManager.CreateParticle("spark", worldPosition,
                                                            Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f, hull);
                }
                if (flames)
                {
                    GameMain.ParticleManager.CreateParticle("explosionfire", ClampParticlePos(worldPosition + Rand.Vector(50f), hull),
                                                            Rand.Vector(Rand.Range(50.0f, 100.0f)), 0.0f, hull);
                }
                if (smoke)
                {
                    GameMain.ParticleManager.CreateParticle("smoke", ClampParticlePos(worldPosition + Rand.Vector(50f), hull),
                                                            Rand.Vector(Rand.Range(1.0f, 10.0f)), 0.0f, hull);
                }
            }

            float displayRange = attack.Range;

            if (displayRange < 0.1f)
            {
                return;
            }

            var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);

            CoroutineManager.StartCoroutine(DimLight(light));

            float cameraDist = Vector2.Distance(GameMain.GameScreen.Cam.Position, worldPosition) / 2.0f;

            GameMain.GameScreen.Cam.Shake = CameraShake * Math.Max((displayRange - cameraDist) / displayRange, 0.0f);

            if (attack.GetStructureDamage(1.0f) > 0.0f)
            {
                RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f));
            }

            if (force == 0.0f && attack.Stun == 0.0f && attack.GetDamage(1.0f) == 0.0f)
            {
                return;
            }

            ApplyExplosionForces(worldPosition, attack.Range, force, attack.GetDamage(1.0f), attack.Stun);

            if (flames && GameMain.Client == null)
            {
                foreach (Item item in Item.ItemList)
                {
                    if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f)
                    {
                        continue;
                    }

                    if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.1f)
                    {
                        continue;
                    }

                    item.ApplyStatusEffects(ActionType.OnFire, 1.0f);

                    if (item.Condition <= 0.0f && GameMain.Server != null)
                    {
                        GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire });
                    }
                }
            }
        }
示例#20
0
        partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull)
        {
            if (shockwave)
            {
                GameMain.ParticleManager.CreateParticle("shockwave", worldPosition,
                                                        Vector2.Zero, 0.0f, hull);
            }

            hull = hull ?? Hull.FindHull(worldPosition, useWorldCoordinates: true);
            bool underwater = hull == null || worldPosition.Y < hull.WorldSurface;

            if (underwater && underwaterBubble)
            {
                var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition, Vector2.Zero, 0.0f, hull);
                if (underwaterExplosion != null)
                {
                    underwaterExplosion.Size      *= MathHelper.Clamp(attack.Range / 150.0f, 0.5f, 10.0f);
                    underwaterExplosion.StartDelay = 0.0f;
                }
            }

            for (int i = 0; i < attack.Range * 0.1f; i++)
            {
                if (!underwater)
                {
                    float particleSpeed = Rand.Range(0.0f, 1.0f);
                    particleSpeed = particleSpeed * particleSpeed * attack.Range;

                    if (flames)
                    {
                        float particleScale = MathHelper.Clamp(attack.Range * 0.0025f, 0.5f, 2.0f);
                        var   flameParticle = GameMain.ParticleManager.CreateParticle("explosionfire",
                                                                                      ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull),
                                                                                      Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
                        if (flameParticle != null)
                        {
                            flameParticle.Size *= particleScale;
                        }
                    }
                    if (smoke)
                    {
                        var smokeParticle = GameMain.ParticleManager.CreateParticle(Rand.Range(0.0f, 1.0f) < 0.5f ? "explosionsmoke" : "smoke",
                                                                                    ClampParticlePos(worldPosition + Rand.Vector((float)System.Math.Sqrt(Rand.Range(0.0f, attack.Range))), hull),
                                                                                    Rand.Vector(Rand.Range(0.0f, particleSpeed)), 0.0f, hull);
                    }
                }
                else if (underwaterBubble)
                {
                    Vector2 bubblePos = Rand.Vector(Rand.Range(0.0f, attack.Range * 0.5f));

                    GameMain.ParticleManager.CreateParticle("risingbubbles", worldPosition + bubblePos,
                                                            Vector2.Zero, 0.0f, hull);

                    if (i < attack.Range * 0.02f)
                    {
                        var underwaterExplosion = GameMain.ParticleManager.CreateParticle("underwaterexplosion", worldPosition + bubblePos,
                                                                                          Vector2.Zero, 0.0f, hull);
                        if (underwaterExplosion != null)
                        {
                            underwaterExplosion.Size *= MathHelper.Clamp(attack.Range / 300.0f, 0.5f, 2.0f) * Rand.Range(0.8f, 1.2f);
                        }
                    }
                }

                if (sparks)
                {
                    GameMain.ParticleManager.CreateParticle("spark", worldPosition,
                                                            Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f, hull);
                }
            }

            if (hull != null && !string.IsNullOrWhiteSpace(decal) && decalSize > 0.0f)
            {
                hull.AddDecal(decal, worldPosition, decalSize);
            }

            if (flash)
            {
                float displayRange = attack.Range;
                if (displayRange < 0.1f)
                {
                    return;
                }

                var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null);
                CoroutineManager.StartCoroutine(DimLight(light));
            }
        }
示例#21
0
        /// <summary>
        /// Control the Character according to player input
        /// </summary>
        public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
        {
            if (DisableControls || GUI.PauseMenuOpen || GUI.SettingsMenuOpen)
            {
                foreach (Key key in keys)
                {
                    if (key == null)
                    {
                        continue;
                    }
                    key.Reset();
                }
            }
            else
            {
                wasFiring |= keys[(int)InputType.Aim].Held && keys[(int)InputType.Shoot].Held;
                for (int i = 0; i < keys.Length; i++)
                {
                    keys[i].SetState();
                }
                //if we were firing (= pressing the aim and shoot keys at the same time)
                //and the fire key is the same as Select or Use, reset the key to prevent accidentally selecting/using items
                if (wasFiring && !keys[(int)InputType.Shoot].Held)
                {
                    if (GameMain.Config.KeyBind(InputType.Shoot).Equals(GameMain.Config.KeyBind(InputType.Select)))
                    {
                        keys[(int)InputType.Select].Reset();
                    }
                    if (GameMain.Config.KeyBind(InputType.Shoot).Equals(GameMain.Config.KeyBind(InputType.Use)))
                    {
                        keys[(int)InputType.Use].Reset();
                    }
                    wasFiring = false;
                }

                float targetOffsetAmount = 0.0f;
                if (moveCam)
                {
                    if (NeedsAir &&
                        pressureProtection < 80.0f &&
                        (AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 0.0f))
                    {
                        float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
                        if (pressure > 0.0f)
                        {
                            float zoomInEffectStrength = MathHelper.Clamp(pressure / 100.0f, 0.1f, 1.0f);
                            cam.Zoom = MathHelper.Lerp(cam.Zoom,
                                                       cam.DefaultZoom + (Math.Max(pressure, 10) / 150.0f) * Rand.Range(0.9f, 1.1f),
                                                       zoomInEffectStrength);

                            pressureParticleTimer += pressure * deltaTime;
                            if (pressureParticleTimer > 10.0f)
                            {
                                GameMain.ParticleManager.CreateParticle(Params.BleedParticleWater, WorldPosition + Rand.Vector(5.0f), Rand.Vector(10.0f));
                                pressureParticleTimer = 0.0f;
                            }
                        }
                    }

                    if (IsHumanoid)
                    {
                        cam.OffsetAmount = 250.0f;// MathHelper.Lerp(cam.OffsetAmount, 250.0f, deltaTime);
                    }
                    else
                    {
                        //increased visibility range when controlling large a non-humanoid
                        cam.OffsetAmount = MathHelper.Clamp(Mass, 250.0f, 1500.0f);
                    }
                }

                cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
                if (AnimController.CurrentHull?.Submarine != null)
                {
                    cursorPosition -= AnimController.CurrentHull.Submarine.Position;
                }

                Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
                if (GUI.PauseMenuOpen)
                {
                    cam.OffsetAmount = targetOffsetAmount = 0.0f;
                }
                else if (Lights.LightManager.ViewTarget is Item item && item.Prefab.FocusOnSelected)
                {
                    cam.OffsetAmount = targetOffsetAmount = item.Prefab.OffsetOnSelected;
                }
示例#22
0
        protected override void UpdateMissionSpecific(float deltaTime)
        {
            if (IsClient)
            {
                foreach (Item item in items)
                {
                    if (item.ParentInventory != null && item.body != null)
                    {
                        item.body.FarseerBody.BodyType = BodyType.Dynamic;
                    }
                }
                return;
            }
            switch (State)
            {
            case 0:
                foreach (Item item in items)
                {
                    if (item.ParentInventory != null && item.body != null)
                    {
                        item.body.FarseerBody.BodyType = BodyType.Dynamic;
                    }
                    if (statusEffectOnApproach.ContainsKey(item))
                    {
                        foreach (Character character in Character.CharacterList)
                        {
                            if (character.IsPlayer && Vector2.DistanceSquared(nestPosition, character.WorldPosition) < approachItemsRadius * approachItemsRadius)
                            {
                                statusEffectOnApproach[item].Apply(statusEffectOnApproach[item].type, 1.0f, item, item);
                                statusEffectOnApproach.Remove(item);
                                break;
                            }
                        }
                    }
                }
                if (monsterPrefabs.Any())
                {
                    foreach (Character character in Character.CharacterList)
                    {
                        if (character.IsPlayer && Vector2.DistanceSquared(nestPosition, character.WorldPosition) < monsterSpawnRadius * monsterSpawnRadius)
                        {
                            foreach (var monster in monsterPrefabs)
                            {
                                int amount = Rand.Range(monster.Item2.X, monster.Item2.Y + 1);
                                for (int i = 0; i < amount; i++)
                                {
                                    Character.Create(monster.Item1.Identifier, nestPosition + Rand.Vector(100.0f), ToolBox.RandomSeed(8), createNetworkEvent: true);
                                }
                            }
                            monsterPrefabs.Clear();
                            break;
                        }
                    }
                }

                //continue when all items are in the sub or destroyed
                if (AllItemsDestroyedOrRetrieved())
                {
                    State = 1;
                }

                break;

            case 1:
                if (!Submarine.MainSub.AtEndExit && !Submarine.MainSub.AtStartExit)
                {
                    return;
                }
                State = 2;
                break;
            }
        }
示例#23
0
        private static void UpdateRandomAmbience(float deltaTime)
        {
            if (ambientSoundTimer > 0.0f)
            {
                ambientSoundTimer -= deltaTime;
            }
            else
            {
                PlaySound(
                    "ambient",
                    new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y) + Rand.Vector(100.0f),
                    Rand.Range(0.5f, 1.0f),
                    1000.0f);

                ambientSoundTimer = Rand.Range(ambientSoundInterval.X, ambientSoundInterval.Y);
            }
        }
示例#24
0
        private List <Vector2> GenerateTunnel(List <Vector2> tunnelNodes, float tunnelLength, List <Vector2> avoidNodes)
        {
            float sectionLength = 1000.0f;

            float currLength = 0.0f;

            while (currLength < tunnelLength)
            {
                Vector2 dir = Rand.Vector(1.0f, Rand.RandSync.Server);

                dir.Y += Math.Sign(tunnelNodes[tunnelNodes.Count - 1].Y - Size.Y / 2) * 0.5f;
                if (tunnelNodes.Count > 1)
                {
                    //keep heading roughly in the same direction as the previous nodes
                    Vector2 prevNodeDiff = tunnelNodes[tunnelNodes.Count - 1] - tunnelNodes[tunnelNodes.Count - 2];
                    if (prevNodeDiff != Vector2.Zero)
                    {
                        dir += Vector2.Normalize(tunnelNodes[tunnelNodes.Count - 1] - tunnelNodes[tunnelNodes.Count - 2]) * 0.5f;
                    }
                }

                float avoidDist = 20000.0f;
                foreach (Vector2 pathNode in avoidNodes)
                {
                    Vector2 diff = tunnelNodes[tunnelNodes.Count - 1] - pathNode;
                    if (diff == Vector2.Zero)
                    {
                        continue;
                    }

                    float dist = diff.Length();
                    if (dist < avoidDist)
                    {
                        dir += (diff / dist) * (1.0f - dist / avoidDist);
                    }
                }

                Vector2 normalizedDir = Vector2.Normalize(dir);

                if (tunnelNodes.Last().Y + normalizedDir.Y > Size.Y)
                {
                    //head back down if the tunnel has reached the top of the level
                    normalizedDir.Y = -normalizedDir.Y;
                }
                else if (tunnelNodes.Last().Y + normalizedDir.Y + normalizedDir.Y < 0.0f ||
                         tunnelNodes.Last().Y + normalizedDir.Y + normalizedDir.Y < SeaFloorTopPos)
                {
                    //head back up if reached the sea floor
                    normalizedDir.Y = -normalizedDir.Y;
                }

                Vector2 nextNode = tunnelNodes.Last() + normalizedDir * sectionLength;

                nextNode.X = MathHelper.Clamp(nextNode.X, 500.0f, Size.X - 500.0f);
                nextNode.Y = MathHelper.Clamp(nextNode.Y, SeaFloorTopPos, Size.Y - 500.0f);
                tunnelNodes.Add(nextNode);
                currLength += sectionLength;
            }

            return(tunnelNodes);
        }
示例#25
0
        public void MoveCamera(float deltaTime, bool allowMove = true, bool allowZoom = true)
        {
            prevPosition = position;
            prevZoom     = zoom;

            float moveSpeed = 20.0f / zoom;

            Vector2 moveCam = Vector2.Zero;

            if (targetPos == Vector2.Zero)
            {
                if (allowMove && GUIComponent.KeyboardDispatcher.Subscriber == null)
                {
                    if (PlayerInput.KeyDown(Keys.LeftShift))
                    {
                        moveSpeed *= 2.0f;
                    }
                    if (PlayerInput.KeyDown(Keys.LeftControl))
                    {
                        moveSpeed *= 0.5f;
                    }

                    if (GameMain.Config.KeyBind(InputType.Left).IsDown())
                    {
                        moveCam.X -= moveSpeed;
                    }
                    if (GameMain.Config.KeyBind(InputType.Right).IsDown())
                    {
                        moveCam.X += moveSpeed;
                    }
                    if (GameMain.Config.KeyBind(InputType.Down).IsDown())
                    {
                        moveCam.Y -= moveSpeed;
                    }
                    if (GameMain.Config.KeyBind(InputType.Up).IsDown())
                    {
                        moveCam.Y += moveSpeed;
                    }
                }

                if (Screen.Selected == GameMain.GameScreen && FollowSub)
                {
                    var closestSub = Submarine.FindClosest(WorldViewCenter);
                    if (closestSub != null)
                    {
                        moveCam += FarseerPhysics.ConvertUnits.ToDisplayUnits(closestSub.Velocity * deltaTime);
                    }
                }

                moveCam = moveCam * deltaTime * 60.0f;

                if (allowZoom)
                {
                    Vector2 mouseInWorld = ScreenToWorld(PlayerInput.MousePosition);
                    Vector2 diffViewCenter;
                    diffViewCenter = ((mouseInWorld - Position) * Zoom);
                    Zoom           = MathHelper.Clamp(zoom + (PlayerInput.ScrollWheelSpeed / 1000.0f) * zoom, GameMain.DebugDraw ? 0.01f : 0.1f, 2.0f);
                    if (!PlayerInput.KeyDown(Keys.F))
                    {
                        Position = mouseInWorld - (diffViewCenter / Zoom);
                    }
                }
            }
            else
            {
                Vector2 mousePos = PlayerInput.MousePosition;

                Vector2 offset = mousePos - new Vector2(resolution.X / 2.0f, resolution.Y / 2.0f);

                offset.X = offset.X / (resolution.X * 0.4f);
                offset.Y = -offset.Y / (resolution.Y * 0.3f);

                if (offset.Length() > 1.0f)
                {
                    offset.Normalize();
                }

                offset = offset * offsetAmount;

                float newZoom = Math.Min(DefaultZoom - Math.Min(offset.Length() / resolution.Y, 1.0f), 1.0f);
                Zoom += (newZoom - zoom) / ZoomSmoothness;

                Vector2 diff = (targetPos + offset) - position;

                moveCam = diff / MoveSmoothness;
            }

            shakeTargetPosition = Rand.Vector(Shake);
            shakePosition       = Vector2.Lerp(shakePosition, shakeTargetPosition, 0.5f);
            Shake = MathHelper.Lerp(Shake, 0.0f, deltaTime * 2.0f);

            Translate(moveCam + shakePosition);
        }
示例#26
0
        public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(explosionPos, null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors         = new Dictionary <Limb, float>();
                Dictionary <Limb, float> damages             = new Dictionary <Limb, float>();
                List <Affliction>        modifiedAfflictions = new List <Affliction>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    if (limb.IsSevered || limb.ignoreCollisions)
                    {
                        continue;
                    }

                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = limb.body.GetMaxExtent();
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion
                    var obstacles = Submarine.PickBodies(limb.SimPosition, explosionPos, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall);
                    foreach (var body in obstacles)
                    {
                        if (body.UserData is Item item)
                        {
                            var door = item.GetComponent <Door>();
                            if (door != null && !door.IsBroken)
                            {
                                distFactor *= 0.01f;
                            }
                        }
                        else if (body.UserData is Structure structure)
                        {
                            int sectionIndex = structure.FindSectionIndex(worldPosition, world: true, clamp: true);
                            if (structure.SectionBodyDisabled(sectionIndex))
                            {
                                continue;
                            }
                            else if (structure.SectionIsLeaking(sectionIndex))
                            {
                                distFactor *= 0.1f;
                            }
                            else
                            {
                                distFactor *= 0.01f;
                            }
                        }
                        else
                        {
                            distFactor *= 0.1f;
                        }
                    }
                    if (distFactor <= 0.05f)
                    {
                        continue;
                    }

                    distFactors.Add(limb, distFactor);

                    modifiedAfflictions.Clear();
                    foreach (Affliction affliction in attack.Afflictions.Keys)
                    {
                        //previously the damage would be divided by the number of limbs (the intention was to prevent characters with more limbs taking more damage from explosions)
                        //that didn't work well on large characters like molochs and endworms: the explosions tend to only damage one or two of their limbs, and since the characters
                        //have lots of limbs, they tended to only take a fraction of the damage they should

                        //now we just divide by 10, which keeps the damage to normal-sized characters roughly the same as before and fixes the large characters
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / 10));
                    }
                    c.LastDamageSource = damageSource;
                    if (attacker == null)
                    {
                        if (damageSource is Item item)
                        {
                            attacker = item.GetComponent <Projectile>()?.User;
                            if (attacker == null)
                            {
                                attacker = item.GetComponent <MeleeWeapon>()?.User;
                            }
                        }
                    }

                    //use a position slightly from the limb's position towards the explosion
                    //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
                    Vector2      dir          = worldPosition - limb.WorldPosition;
                    Vector2      hitPos       = limb.WorldPosition + (dir.LengthSquared() <= 0.001f ? Rand.Vector(1.0f) : Vector2.Normalize(dir)) * 0.01f;
                    AttackResult attackResult = c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);
                    damages.Add(limb, attackResult.Damage);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulse      = limbDiff * distFactor * force;
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.2f);
                    }
                }

                //sever joints
                if (attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (limb.character.Removed || limb.Removed)
                        {
                            continue;
                        }
                        if (limb.IsSevered)
                        {
                            continue;
                        }
                        if (!c.IsDead && !limb.CanBeSeveredAlive)
                        {
                            continue;
                        }
                        if (distFactors.TryGetValue(limb, out float distFactor))
                        {
                            if (damages.TryGetValue(limb, out float damage))
                            {
                                c.TrySeverLimbJoints(limb, attack.SeverLimbsProbability * distFactor, damage, allowBeheading: true);
                            }
                        }
                    }
                }
            }
        }
示例#27
0
        public static void Update(float deltaTime)
        {
            UpdateMusic(deltaTime);

            if (startDrone != null && !startDrone.IsPlaying)
            {
                startDrone.Remove();
                startDrone = null;
            }

            //stop submarine ambient sounds if no sub is loaded
            if (Submarine.MainSub == null)
            {
                for (int i = 0; i < waterAmbienceIndexes.Length; i++)
                {
                    if (waterAmbienceIndexes[i] <= 0)
                    {
                        continue;
                    }

                    SoundManager.Stop(waterAmbienceIndexes[i]);
                    waterAmbienceIndexes[i] = 0;
                }
                return;
            }

            float ambienceVolume = 0.8f;
            float lowpassHFGain  = 1.0f;

            if (Character.Controlled != null)
            {
                AnimController animController = Character.Controlled.AnimController;
                if (animController.HeadInWater)
                {
                    ambienceVolume  = 1.0f;
                    ambienceVolume += animController.Limbs[0].LinearVelocity.Length();

                    lowpassHFGain = 0.2f;
                }

                lowpassHFGain *= Character.Controlled.LowPassMultiplier;
            }

            //how fast the sub is moving, scaled to 0.0 -> 1.0
            float movementSoundVolume = 0.0f;

            foreach (Submarine sub in Submarine.Loaded)
            {
                float movementFactor = (sub.Velocity == Vector2.Zero) ? 0.0f : sub.Velocity.Length() / 10.0f;
                movementFactor = MathHelper.Clamp(movementFactor, 0.0f, 1.0f);

                if (Character.Controlled == null || Character.Controlled.Submarine != sub)
                {
                    float dist = Vector2.Distance(GameMain.GameScreen.Cam.WorldViewCenter, sub.WorldPosition);
                    movementFactor = movementFactor / Math.Max(dist / 1000.0f, 1.0f);
                }

                movementSoundVolume = Math.Max(movementSoundVolume, movementFactor);
            }

            if (ambientSoundTimer > 0.0f)
            {
                ambientSoundTimer -= (float)Timing.Step;
            }
            else
            {
                PlaySound(
                    "ambient",
                    Rand.Range(0.5f, 1.0f),
                    1000.0f,
                    new Vector2(Sound.CameraPos.X, Sound.CameraPos.Y) + Rand.Vector(100.0f));

                ambientSoundTimer = Rand.Range(ambientSoundInterval.X, ambientSoundInterval.Y);
            }

            SoundManager.LowPassHFGain = lowpassHFGain;
            waterAmbienceIndexes[0]    = waterAmbiences[0].Loop(waterAmbienceIndexes[0], ambienceVolume * (1.0f - movementSoundVolume));
            waterAmbienceIndexes[1]    = waterAmbiences[1].Loop(waterAmbienceIndexes[1], ambienceVolume * movementSoundVolume);
        }
示例#28
0
        private void UpdateSounds(Character character, float deltaTime)
        {
            if (soundTimer < MathHelper.Lerp(maxSoundInterval, minSoundInterval, Strength / 100.0f))
            {
                soundTimer += deltaTime;
                return;
            }

            float impactStrength = MathHelper.Lerp(0.1f, 1.0f, Strength / 100.0f);

            SoundPlayer.PlayDamageSound("StructureBlunt", Rand.Range(10.0f, 1000.0f), character.WorldPosition + Rand.Vector(500.0f));
            GameMain.GameScreen.Cam.Shake           = impactStrength * 10.0f;
            GameMain.GameScreen.Cam.AngularVelocity = Rand.Range(-impactStrength, impactStrength);
            soundTimer = 0.0f;
        }
示例#29
0
        partial void Splash(Limb limb, Hull limbHull)
        {
            //create a splash particle
            for (int i = 0; i < MathHelper.Clamp(Math.Abs(limb.LinearVelocity.Y), 1.0f, 5.0f); i++)
            {
                var splash = GameMain.ParticleManager.CreateParticle("watersplash",
                                                                     new Vector2(limb.WorldPosition.X, limbHull.WorldSurface),
                                                                     new Vector2(0.0f, Math.Abs(-limb.LinearVelocity.Y * 20.0f)) + Rand.Vector(Math.Abs(limb.LinearVelocity.Y * 10)),
                                                                     Rand.Range(0.0f, MathHelper.TwoPi), limbHull);

                if (splash != null)
                {
                    splash.Size *= MathHelper.Clamp(Math.Abs(limb.LinearVelocity.Y) * 0.1f, 1.0f, 2.0f);
                }
            }

            GameMain.ParticleManager.CreateParticle("bubbles",
                                                    new Vector2(limb.WorldPosition.X, limbHull.WorldSurface),
                                                    limb.LinearVelocity * 0.001f,
                                                    0.0f, limbHull);

            //if the Character dropped into water, create a wave
            if (limb.LinearVelocity.Y < 0.0f)
            {
                if (splashSoundTimer <= 0.0f)
                {
                    SoundPlayer.PlaySplashSound(limb.WorldPosition, Math.Abs(limb.LinearVelocity.Y) + Rand.Range(-5.0f, 0.0f));
                    splashSoundTimer = 0.5f;
                }

                //+ some extra bubbles to follow the character underwater
                GameMain.ParticleManager.CreateParticle("bubbles",
                                                        new Vector2(limb.WorldPosition.X, limbHull.WorldSurface),
                                                        limb.LinearVelocity * 10.0f,
                                                        0.0f, limbHull);
            }
        }
示例#30
0
        public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

            //long range for the broad distance check, because large characters may still be in range even if their collider isn't
            float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f);

            foreach (Character c in Character.CharacterList)
            {
                if (!c.Enabled ||
                    Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange ||
                    Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange)
                {
                    continue;
                }

                Vector2 explosionPos = worldPosition;
                if (c.Submarine != null)
                {
                    explosionPos -= c.Submarine.Position;
                }

                Hull hull       = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false);
                bool underWater = hull == null || explosionPos.Y < hull.Surface;

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                Dictionary <Limb, float> distFactors = new Dictionary <Limb, float>();
                foreach (Limb limb in c.AnimController.Limbs)
                {
                    float dist = Vector2.Distance(limb.WorldPosition, worldPosition);

                    //calculate distance from the "outer surface" of the physics body
                    //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose
                    float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius);
                    dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > attack.Range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / attack.Range;

                    //solid obstacles between the explosion and the limb reduce the effect of the explosion by 90%
                    if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null)
                    {
                        distFactor *= 0.1f;
                    }

                    distFactors.Add(limb, distFactor);

                    List <Affliction> modifiedAfflictions = new List <Affliction>();
                    foreach (Affliction affliction in attack.Afflictions.Keys)
                    {
                        modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length));
                    }
                    c.LastDamageSource = damageSource;
                    if (attacker == null)
                    {
                        if (damageSource is Item item)
                        {
                            attacker = item.GetComponent <Projectile>()?.User;
                            if (attacker == null)
                            {
                                attacker = item.GetComponent <MeleeWeapon>()?.User;
                            }
                        }
                    }

                    //use a position slightly from the limb's position towards the explosion
                    //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods
                    Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f;
                    c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker);

                    if (attack.StatusEffects != null && attack.StatusEffects.Any())
                    {
                        attack.SetUser(attacker);
                        var statusEffectTargets = new List <ISerializableEntity>()
                        {
                            c, limb
                        };
                        foreach (StatusEffect statusEffect in attack.StatusEffects)
                        {
                            statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets);
                            statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets);
                        }
                    }

                    if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f))
                    {
                        Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition);
                        if (!MathUtils.IsValid(limbDiff))
                        {
                            limbDiff = Rand.Vector(1.0f);
                        }
                        Vector2 impulse      = limbDiff * distFactor * force;
                        Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius;
                        limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    }
                }

                //sever joints
                if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (!distFactors.ContainsKey(limb))
                        {
                            continue;
                        }
                        if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
                        {
                            c.TrySeverLimbJoints(limb, 1.0f);
                        }
                    }
                }
            }
        }