Пример #1
0
        private void UpdateOutsideColliderPos(Hull hull)
        {
            outsideColliderNormal = null;
            outsideColliderPos    = null;

            if (Submarine == null)
            {
                return;
            }

            Vector2 rayDir;

            if (IsHorizontal)
            {
                rayDir = new Vector2(Math.Sign(rect.Center.X - hull.Rect.Center.X), 0);
            }
            else
            {
                rayDir = new Vector2(0, Math.Sign((rect.Y - rect.Height / 2) - (hull.Rect.Y - hull.Rect.Height / 2)));
            }

            Vector2 rayStart = ConvertUnits.ToSimUnits(WorldPosition);
            Vector2 rayEnd   = rayStart + rayDir * 500.0f;

            if (Submarine.CheckVisibility(rayStart, rayEnd) != null)
            {
                outsideColliderNormal = -rayDir;
                outsideColliderPos    = Submarine.LastPickedPosition;
            }
        }
        private void UpdateNone(float deltaTime)
        {
            attackingLimb  = null;
            coolDownTimer -= deltaTime;

            if (Character.Submarine == null && SimPosition.Y < ConvertUnits.ToSimUnits(SubmarineBody.DamageDepth * 0.5f))
            {
                //steer straight up if very deep
                steeringManager.SteeringManual(deltaTime, Vector2.UnitY);
                return;
            }

            if (attachToWalls && Character.Submarine == null && Level.Loaded != null)
            {
                raycastTimer -= deltaTime;
                //check if there are any walls nearby the character could attach to
                if (raycastTimer < 1.0f)
                {
                    wallAttackPos = Vector2.Zero;

                    var cells = Level.Loaded.GetCells(WorldPosition, 1);
                    if (cells.Count > 0)
                    {
                        Body closestBody = Submarine.CheckVisibility(Character.SimPosition, ConvertUnits.ToSimUnits(cells[0].Center));
                        if (closestBody != null && closestBody.UserData is Voronoi2.VoronoiCell)
                        {
                            wallAttackPos = Submarine.LastPickedPosition;
                        }
                    }
                    raycastTimer = RaycastInterval;
                }
            }
            else
            {
                wallAttackPos = Vector2.Zero;
            }

            if (wallAttackPos == Vector2.Zero)
            {
                //wander around randomly
                steeringManager.SteeringAvoid(deltaTime, 0.1f);
                steeringManager.SteeringWander(0.5f);
                return;
            }

            float dist = Vector2.Distance(SimPosition, wallAttackPos);

            if (dist < Math.Max(Math.Max(Character.AnimController.Collider.radius, Character.AnimController.Collider.width), Character.AnimController.Collider.height) * 1.2f)
            {
                //close enough to a wall -> attach
                Character.AnimController.Collider.MoveToPos(wallAttackPos, 1.0f);
                steeringManager.Reset();
            }
            else
            {
                //move closer to the wall
                steeringManager.SteeringAvoid(deltaTime, 0.1f);
                steeringManager.SteeringSeek(wallAttackPos);
            }
        }
Пример #3
0
        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);
        }
Пример #4
0
        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
                {
                    Structure closestStructure = closestBody.UserData as Structure;
                    if (closestStructure != null)
                    {
                        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 item = (Item)closestBody.UserData;
                        avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition);
                    }
                    else
                    {
                        avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition);
                    }
                }
            }
            else
            {
                rayCastTimer -= deltaTime;
            }

            return(avoidSteering * speed);
        }
Пример #5
0
        public static void ApplyExplosionForces(Vector2 worldPosition, float range, float force, float damage = 0.0f, float stun = 0.0f)
        {
            if (range <= 0.0f)
            {
                return;
            }

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

                explosionPos = ConvertUnits.ToSimUnits(explosionPos);

                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 - FarseerPhysics.ConvertUnits.ToDisplayUnits(limbRadius));

                    if (dist > range)
                    {
                        continue;
                    }

                    float distFactor = 1.0f - dist / 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;
                    }

                    c.AddDamage(limb.WorldPosition, DamageType.None,
                                damage / c.AnimController.Limbs.Length * distFactor, 0.0f, stun * distFactor, false);

                    if (limb.WorldPosition == worldPosition)
                    {
                        continue;
                    }

                    if (force > 0.0f)
                    {
                        limb.body.ApplyLinearImpulse(Vector2.Normalize(limb.WorldPosition - worldPosition) * distFactor * force);
                    }
                }
            }
        }
Пример #6
0
        private void UpdateOutsideColliderPos(Hull hull)
        {
            if (Submarine == null || IsRoomToRoom || Level.Loaded == null)
            {
                return;
            }

            Vector2 rayDir;

            if (IsHorizontal)
            {
                rayDir = new Vector2(Math.Sign(rect.Center.X - hull.Rect.Center.X), 0);
            }
            else
            {
                rayDir = new Vector2(0, Math.Sign((rect.Y - rect.Height / 2) - (hull.Rect.Y - hull.Rect.Height / 2)));
            }

            Vector2 rayStart = ConvertUnits.ToSimUnits(WorldPosition);
            Vector2 rayEnd   = rayStart + rayDir * 500.0f;

            var levelCells = Level.Loaded.GetCells(WorldPosition, searchDepth: 1);

            foreach (var cell in levelCells)
            {
                if (cell.IsPointInside(WorldPosition))
                {
                    outsideCollisionBlocker.Enabled = true;
                    Vector2 colliderPos      = rayStart - Submarine.SimPosition;
                    float   colliderRotation = MathUtils.VectorToAngle(rayDir) - MathHelper.PiOver2;
                    outsideCollisionBlocker.SetTransformIgnoreContacts(ref colliderPos, colliderRotation);
                    return;
                }
            }

            var blockingBody = Submarine.CheckVisibility(rayStart, rayEnd);

            if (blockingBody != null)
            {
                //if the ray hit the body of the submarine itself (for example, if there's 2 layers of walls) we can ignore it
                if (blockingBody.UserData == Submarine)
                {
                    return;
                }
                outsideCollisionBlocker.Enabled = true;
                Vector2 colliderPos      = Submarine.LastPickedPosition - Submarine.SimPosition;
                float   colliderRotation = MathUtils.VectorToAngle(rayDir) - MathHelper.PiOver2;
                outsideCollisionBlocker.SetTransformIgnoreContacts(ref colliderPos, colliderRotation);
            }
            else
            {
                outsideCollisionBlocker.Enabled = false;
            }
        }
Пример #7
0
        protected virtual Vector2 DoSteeringAvoid(float deltaTime, float lookAheadDistance, float weight, Vector2?heading = null)
        {
            if (steering == Vector2.Zero || host.Steering == Vector2.Zero)
            {
                return(Vector2.Zero);
            }

            float maxDistance = lookAheadDistance;

            if (Timing.TotalTime >= lastRayCastTime + RayCastInterval)
            {
                avoidRayCastHit   = false;
                AvoidLookAheadPos = host.SimPosition + Vector2.Normalize(host.Steering) * maxDistance;
                lastRayCastTime   = (float)Timing.TotalTime;
                Body closestBody = Submarine.CheckVisibility(host.SimPosition, AvoidLookAheadPos);
                if (closestBody != null)
                {
                    avoidRayCastHit         = true;
                    AvoidRayCastHitPosition = Submarine.LastPickedPosition;
                    AvoidDir = Submarine.LastPickedNormal;
                    //add a bit of randomness
                    AvoidDir = MathUtils.RotatePoint(AvoidDir, Rand.Range(-0.15f, 0.15f));
                    //wait a bit longer for the next raycast
                    lastRayCastTime += RayCastInterval;
                }
            }

            if (AvoidDir.LengthSquared() < 0.0001f)
            {
                return(Vector2.Zero);
            }

            //if raycast hit nothing, lerp avoid dir to zero
            if (!avoidRayCastHit)
            {
                AvoidDir -= Vector2.Normalize(AvoidDir) * deltaTime * 0.5f;
            }

            Vector2 diff = AvoidRayCastHitPosition - host.SimPosition;
            float   dist = diff.Length();

            //> 0 when heading in the same direction as the obstacle, < 0 when away from it
            float dot = MathHelper.Clamp(Vector2.Dot(diff / dist, host.Steering), 0.0f, 1.0f);

            if (dot < 0)
            {
                return(Vector2.Zero);
            }

            return(AvoidDir * dot * weight * MathHelper.Clamp(1.0f - dist / lookAheadDistance, 0.0f, 1.0f));
        }
Пример #8
0
        private WayPoint FindClosestOutside(IEnumerable <WayPoint> waypointList, float tolerance, Body ignoredBody = null, IEnumerable <WayPoint> ignored = null, Func <WayPoint, bool> filter = null)
        {
            float    closestDist = 0;
            WayPoint closest     = null;

            foreach (WayPoint wp in waypointList)
            {
                if (wp.SpawnType != SpawnType.Path || wp == this)
                {
                    continue;
                }
                // Ignore if already linked
                if (linkedTo.Contains(wp))
                {
                    continue;
                }
                if (ignored != null && ignored.Contains(wp))
                {
                    continue;
                }
                if (filter != null && !filter(wp))
                {
                    continue;
                }
                float sqrDist = Vector2.DistanceSquared(Position, wp.Position);
                if (closest == null || sqrDist < closestDist)
                {
                    var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);
                    if (body != null && body != ignoredBody && !(body.UserData is Submarine))
                    {
                        if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
                        {
                            continue;
                        }
                    }
                    closestDist = sqrDist;
                    closest     = wp;
                }
            }
            return(closest);
        }
Пример #9
0
        public SteeringPath FindPath(Vector2 start, Vector2 end)
        {
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
            sw.Start();

            float    closestDist = 0.0f;
            PathNode startNode   = null;

            foreach (PathNode node in nodes)
            {
                Vector2 nodePos = node.Position;

                float dist = System.Math.Abs(start.X - nodePos.X) +
                             System.Math.Abs(start.Y - nodePos.Y) * 10.0f; //higher cost for vertical movement

                //prefer nodes that are closer to the end position
                dist += Vector2.Distance(end, nodePos) / 10.0f;

                if (dist < closestDist || startNode == null)
                {
                    //if searching for a path inside the sub, make sure the waypoint is visible
                    if (insideSubmarine)
                    {
                        var body = Submarine.PickBody(
                            start, node.Waypoint.SimPosition, null,
                            Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs | Physics.CollisionPlatform);

                        if (body != null)
                        {
                            if (body.UserData is Structure && !((Structure)body.UserData).IsPlatform)
                            {
                                continue;
                            }
                            if (body.UserData is Item && body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
                            {
                                continue;
                            }
                        }
                    }

                    closestDist = dist;
                    startNode   = node;
                }
            }

            if (startNode == null)
            {
                DebugConsole.NewMessage("Pathfinding error, couldn't find a start node", Color.DarkRed);

                return(new SteeringPath());
            }

            closestDist = 0.0f;
            PathNode endNode = null;

            foreach (PathNode node in nodes)
            {
                Vector2 nodePos = node.Position;

                float dist = Vector2.Distance(end, nodePos);
                if (dist < closestDist || endNode == null)
                {
                    //if searching for a path inside the sub, make sure the waypoint is visible
                    if (insideSubmarine)
                    {
                        var body = Submarine.CheckVisibility(end, node.Waypoint.SimPosition);
                        if (body != null && body.UserData is Structure)
                        {
                            continue;
                        }
                    }

                    closestDist = dist;
                    endNode     = node;
                }
            }

            if (endNode == null)
            {
                DebugConsole.NewMessage("Pathfinding error, couldn't find an end node", Color.DarkRed);
                return(new SteeringPath());
            }


            var path = FindPath(startNode, endNode);

            sw.Stop();
            System.Diagnostics.Debug.WriteLine("findpath: " + sw.ElapsedMilliseconds + " ms");

            return(path);
        }
Пример #10
0
        private WayPoint FindClosest(int dir, bool horizontalSearch, Vector2 tolerance, Body ignoredBody = null)
        {
            if (dir != -1 && dir != 1)
            {
                return(null);
            }

            float    closestDist = 0.0f;
            WayPoint closest     = null;


            foreach (WayPoint wp in WayPointList)
            {
                if (wp.SpawnType != SpawnType.Path || wp == this)
                {
                    continue;
                }

                float diff = 0.0f;
                if (horizontalSearch)
                {
                    if ((wp.Position.Y - Position.Y) < tolerance.X || (wp.Position.Y - Position.Y) > tolerance.Y)
                    {
                        continue;
                    }

                    diff = wp.Position.X - Position.X;
                }
                else
                {
                    if ((wp.Position.X - Position.X) < tolerance.X || (wp.Position.X - Position.X) > tolerance.Y)
                    {
                        continue;
                    }

                    diff = wp.Position.Y - Position.Y;
                }

                if (Math.Sign(diff) != dir)
                {
                    continue;
                }

                float dist = Vector2.Distance(wp.Position, Position);
                if (closest == null || dist < closestDist)
                {
                    var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, true, true, false);
                    if (body != null && body != ignoredBody && !(body.UserData is Submarine))
                    {
                        if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
                        {
                            continue;
                        }
                    }

                    closestDist = dist;
                    closest     = wp;
                }
            }

            return(closest);
        }
Пример #11
0
        public override void DragCharacter(Character target, float deltaTime)
        {
            if (target == null)
            {
                return;
            }
            Limb mouthLimb = GetLimb(LimbType.Head);

            if (mouthLimb == null)
            {
                return;
            }

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                //stop dragging if there's something between the pull limb and the target
                Vector2 sourceSimPos = SimplePhysicsEnabled ? character.SimPosition : mouthLimb.SimPosition;
                Vector2 targetSimPos = target.SimPosition;
                if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
                {
                    targetSimPos -= character.Submarine.SimPosition;
                }
                else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
                {
                    sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
                }
                var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
                if (body != null)
                {
                    character.DeselectCharacter();
                    return;
                }
            }

            float dmg      = character.Params.EatingSpeed;
            float eatSpeed = dmg / ((float)Math.Sqrt(Math.Max(target.Mass, 1)) * 10);

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos          = SimplePhysicsEnabled ? character.SimPosition : GetMouthPosition().Value;
            Vector2 attackSimPosition = character.Submarine == null?ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;

            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   extent   = Math.Max(mouthLimb.body.GetMaxExtent(), 1);

            if (limbDiff.LengthSquared() < extent * extent)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                float dragForce = MathHelper.Clamp(eatSpeed * 10, 0, 40);
                if (dragForce > 0.1f)
                {
                    target.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + dragForce));
                    target.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation, dragForce * 2);
                    target.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + dragForce));
                }

                if (InWater)
                {
                    //pull the character's mouth to the target character (again with a fluctuating force)
                    float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                    mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                }
                else
                {
                    float force = (float)Math.Sin(eatTimer * 100) * mouthLimb.Mass;
                    mouthLimb.body.ApplyLinearImpulse(Vector2.UnitY * force * 2, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
                    mouthLimb.body.ApplyTorque(-force * 50);
                }
                var jaw = GetLimb(LimbType.Jaw);
                if (jaw != null)
                {
                    jaw.body.ApplyTorque(-(float)Math.Sin(eatTimer * 150) * jaw.Mass * 25);
                }

                character.ApplyStatusEffects(ActionType.OnEating, deltaTime);

                float particleFrequency = MathHelper.Clamp(eatSpeed / 2, 0.02f, 0.5f);
                if (Rand.Value() < particleFrequency / 6)
                {
                    target.AnimController.MainLimb.AddDamage(target.SimPosition, dmg, 0, 0, false);
                }
                if (Rand.Value() < particleFrequency)
                {
                    target.AnimController.MainLimb.AddDamage(target.SimPosition, 0, dmg, 0, false);
                }
                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    bool CanBeSevered(LimbJoint j) => !j.IsSevered && j.CanBeSevered && j.LimbA != null && !j.LimbA.IsSevered && j.LimbB != null && !j.LimbB.IsSevered;

                    //keep severing joints until there is only one limb left
                    var nonSeveredJoints = target.AnimController.LimbJoints.Where(CanBeSevered);
                    if (nonSeveredJoints.None())
                    {
                        //only one limb left, the character is now full eaten
                        Entity.Spawner?.AddToRemoveQueue(target);
                        character.SelectedCharacter = null;
                    }
                    else //sever a random joint
                    {
                        target.AnimController.SeverLimbJoint(nonSeveredJoints.GetRandom());
                    }
                }
            }
            else
            {
                character.SelectedCharacter = null;
            }
        }
Пример #12
0
        /// <summary>
        /// Control the Character according to player input
        /// </summary>
        public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
        {
            if (DisableControls)
            {
                foreach (Key key in keys)
                {
                    if (key == null)
                    {
                        continue;
                    }
                    key.Reset();
                }
            }
            else
            {
                for (int i = 0; i < keys.Length; i++)
                {
                    keys[i].SetState();
                }

                if (moveCam)
                {
                    if (needsAir &&
                        pressureProtection < 80.0f &&
                        (AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 50.0f))
                    {
                        float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;

                        cam.Zoom = MathHelper.Lerp(cam.Zoom,
                                                   (pressure / 50.0f) * Rand.Range(1.0f, 1.05f),
                                                   (pressure - 50.0f) / 50.0f);
                    }

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

                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 = 0.0f;
                }
                else if (SelectedConstruction != null && SelectedConstruction.Components.Any(ic => (ic.GuiFrame != null && GUI.IsMouseOn(ic.GuiFrame))))
                {
                    cam.OffsetAmount = 0.0f;
                }
                else if (Lights.LightManager.ViewTarget == this && Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f)
                {
                    if (GUI.PauseMenuOpen || IsUnconscious)
                    {
                        if (deltaTime > 0.0f)
                        {
                            cam.OffsetAmount = 0.0f;
                        }
                    }
                    else if (Lights.LightManager.ViewTarget == this && Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f)
                    {
                        Body      body      = Submarine.CheckVisibility(AnimController.Limbs[0].SimPosition, mouseSimPos);
                        Structure structure = body == null ? null : body.UserData as Structure;

                        float sightDist = Submarine.LastPickedFraction;
                        if (body?.UserData is Structure && !((Structure)body.UserData).CastShadow)
                        {
                            sightDist = 1.0f;
                        }
                        cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, Math.Max(250.0f, sightDist * 500.0f), 0.05f);
                    }
                }

                if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)))
                {
                    if (GameMain.Client != null)
                    {
                        //emulate a Select input to get the character to deselect the item server-side
                        keys[(int)InputType.Select].Hit = true;
                    }
                    //reset focus to prevent us from accidentally interacting with another entity
                    focusedItem          = null;
                    focusedCharacter     = null;
                    findFocusedTimer     = 0.2f;
                    SelectedConstruction = null;
                }

                DoInteractionUpdate(deltaTime, mouseSimPos);
            }

            DisableControls = false;
        }
Пример #13
0
        public SteeringPath FindPath(Vector2 start, Vector2 end, string errorMsgStr = null)
        {
            float    closestDist = 0.0f;
            PathNode startNode   = null;

            foreach (PathNode node in nodes)
            {
                Vector2 nodePos = node.Position;

                float xDiff = System.Math.Abs(start.X - nodePos.X);
                float yDiff = System.Math.Abs(start.Y - nodePos.Y);

                if (yDiff > 1.0f && node.Waypoint.Ladders == null && node.Waypoint.Stairs == null)
                {
                    yDiff += 10.0f;
                }

                float dist = xDiff + (insideSubmarine ? yDiff * 10.0f : yDiff); //higher cost for vertical movement when inside the sub

                //prefer nodes that are closer to the end position
                dist += (Math.Abs(end.X - nodePos.X) + Math.Abs(end.Y - nodePos.Y)) / 2.0f;
                //much higher cost to waypoints that are outside
                if (node.Waypoint.CurrentHull == null && insideSubmarine)
                {
                    dist *= 10.0f;
                }
                if (dist < closestDist || startNode == null)
                {
                    //if searching for a path inside the sub, make sure the waypoint is visible
                    if (insideSubmarine)
                    {
                        var body = Submarine.PickBody(
                            start, node.Waypoint.SimPosition, null,
                            Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs | Physics.CollisionPlatform);

                        if (body != null)
                        {
                            if (body.UserData is Submarine)
                            {
                                continue;
                            }
                            if (body.UserData is Structure && !((Structure)body.UserData).IsPlatform)
                            {
                                continue;
                            }
                            if (body.UserData is Item && body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
                            {
                                continue;
                            }
                        }
                    }

                    closestDist = dist;
                    startNode   = node;
                }
            }

            if (startNode == null)
            {
                DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. " + errorMsgStr, Color.DarkRed);

                return(new SteeringPath(true));
            }

            closestDist = 0.0f;
            PathNode endNode = null;

            foreach (PathNode node in nodes)
            {
                Vector2 nodePos = node.Position;

                float dist = Vector2.Distance(end, nodePos);
                if (insideSubmarine)
                {
                    //much higher cost to waypoints that are outside
                    if (node.Waypoint.CurrentHull == null)
                    {
                        dist *= 10.0f;
                    }
                    //avoid stopping at a doorway
                    if (node.Waypoint.ConnectedDoor != null)
                    {
                        dist *= 10.0f;
                    }
                }
                if (dist < closestDist || endNode == null)
                {
                    //if searching for a path inside the sub, make sure the waypoint is visible
                    if (insideSubmarine)
                    {
                        // TODO: for some reason fails to find the path when the sub is flooding. Disabling this check helps fixes it, but we can't disable it
                        var body = Submarine.CheckVisibility(end, node.Waypoint.SimPosition);
                        if (body != null && body.UserData is Structure)
                        {
                            continue;
                        }
                    }

                    closestDist = dist;
                    endNode     = node;
                }
            }

            if (endNode == null)
            {
                DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed);
                return(new SteeringPath(true));
            }

            var path = FindPath(startNode, endNode);

            return(path);
        }
Пример #14
0
        private void GetTargetEntity()
        {
            targetEntity = null;

            if (Character.AnimController.CurrentHull != null)
            {
                wallAttackPos = Vector2.Zero;
                return;
            }

            //check if there's a wall between the target and the Character
            Vector2 rayStart = Character.SimPosition;
            Vector2 rayEnd   = selectedAiTarget.SimPosition;

            if (selectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
            {
                rayStart -= ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
            }

            Body closestBody = Submarine.CheckVisibility(rayStart, rayEnd);

            if (Submarine.LastPickedFraction == 1.0f || closestBody == null)
            {
                wallAttackPos = Vector2.Zero;
                return;
            }

            Structure wall = closestBody.UserData as Structure;

            if (wall == null)
            {
                wallAttackPos = Submarine.LastPickedPosition;
                if (selectedAiTarget.Entity.Submarine != null && Character.Submarine == null)
                {
                    wallAttackPos -= ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position);
                }
            }
            else
            {
                int sectionIndex = wall.FindSectionIndex(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition));

                float sectionDamage = wall.SectionDamage(sectionIndex);
                for (int i = sectionIndex - 2; i <= sectionIndex + 2; i++)
                {
                    if (wall.SectionBodyDisabled(i))
                    {
                        sectionIndex = i;
                        break;
                    }
                    if (wall.SectionDamage(i) > sectionDamage)
                    {
                        sectionIndex = i;
                    }
                }
                wallAttackPos = wall.SectionPosition(sectionIndex);
                //if (wall.Submarine != null) wallAttackPos += wall.Submarine.Position;
                wallAttackPos = ConvertUnits.ToSimUnits(wallAttackPos);
            }

            targetEntity = closestBody.UserData as IDamageable;
        }
Пример #15
0
        protected virtual Vector2 DoSteeringAvoid(float deltaTime, float lookAheadDistance, float weight)
        {
            if (steering == Vector2.Zero || host.Steering == Vector2.Zero)
            {
                return(Vector2.Zero);
            }

            float maxDistance = lookAheadDistance;

            if (rayCastTimer <= 0.0f)
            {
                Vector2 ahead = host.SimPosition + Vector2.Normalize(host.Steering) * maxDistance;
                rayCastTimer = RayCastInterval;
                Body closestBody = Submarine.CheckVisibility(host.SimPosition, ahead);
                if (closestBody == null)
                {
                    avoidObstaclePos = null;
                    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;
                        }

                        avoidObstaclePos = obstaclePosition;
                        //avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - obstaclePosition);
                    }

                    /*else if (closestBody.UserData is Item)
                     * {
                     *  avoidSteering = Vector2.Normalize(Submarine.LastPickedPosition - item.SimPosition);
                     * }*/
                    else
                    {
                        avoidObstaclePos = Submarine.LastPickedPosition;
                        //avoidSteering = Vector2.Normalize(host.SimPosition - Submarine.LastPickedPosition);
                    }
                }
            }
            else
            {
                rayCastTimer -= deltaTime;
            }

            if (!avoidObstaclePos.HasValue)
            {
                return(Vector2.Zero);
            }

            Vector2 diff = avoidObstaclePos.Value - host.SimPosition;
            float   dist = diff.Length();

            if (dist > maxDistance)
            {
                return(Vector2.Zero);
            }

            return(-diff * (1.0f - dist / maxDistance) * weight);
        }
Пример #16
0
        /// <summary>
        /// Control the Character according to player input
        /// </summary>
        public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
        {
            if (!DisableControls)
            {
                for (int i = 0; i < keys.Length; i++)
                {
                    keys[i].SetState();
                }
            }
            else
            {
                foreach (Key key in keys)
                {
                    if (key == null)
                    {
                        continue;
                    }
                    key.Reset();
                }
            }

            if (moveCam)
            {
                if (needsAir &&
                    pressureProtection < 80.0f &&
                    (AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 50.0f))
                {
                    float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;

                    cam.Zoom = MathHelper.Lerp(cam.Zoom,
                                               (pressure / 50.0f) * Rand.Range(1.0f, 1.05f),
                                               (pressure - 50.0f) / 50.0f);
                }

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

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

            Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);

            if (moveCam)
            {
                if (DebugConsole.IsOpen || GUI.PauseMenuOpen || IsUnconscious ||
                    (GameMain.GameSession?.CrewManager?.CrewCommander != null && GameMain.GameSession.CrewManager.CrewCommander.IsOpen))
                {
                    if (deltaTime > 0.0f)
                    {
                        cam.OffsetAmount = 0.0f;
                    }
                }
                else if (Lights.LightManager.ViewTarget == this && Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f)
                {
                    Body      body      = Submarine.CheckVisibility(AnimController.Limbs[0].SimPosition, mouseSimPos);
                    Structure structure = body == null ? null : body.UserData as Structure;

                    float sightDist = Submarine.LastPickedFraction;
                    if (body?.UserData is Structure && !((Structure)body.UserData).CastShadow)
                    {
                        sightDist = 1.0f;
                    }
                    cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, Math.Max(250.0f, sightDist * 500.0f), 0.05f);
                }
            }

            DoInteractionUpdate(deltaTime, mouseSimPos);

            DisableControls = false;
        }
Пример #17
0
        protected virtual Vector2 DoSteeringAvoid(float deltaTime, float lookAheadDistance, float weight, Vector2?heading = null)
        {
            if (steering == Vector2.Zero || host.Steering == Vector2.Zero)
            {
                return(Vector2.Zero);
            }
            float maxDistance = lookAheadDistance;

            if (rayCastTimer <= 0.0f)
            {
                Vector2 ahead = host.SimPosition + Vector2.Normalize(host.Steering) * maxDistance;
                rayCastTimer = RayCastInterval;
                Body closestBody = Submarine.CheckVisibility(host.SimPosition, ahead);
                if (closestBody == null)
                {
                    avoidObstaclePos = null;
                    return(Vector2.Zero);
                }
                else
                {
                    // TODO: Doesn't take items into account (like turrets)
                    if (closestBody.UserData is Structure closestStructure)
                    {
                        Vector2 obstaclePosition = Submarine.LastPickedPosition;
                        if (closestStructure.IsHorizontal)
                        {
                            obstaclePosition.Y = closestStructure.SimPosition.Y;
                        }
                        else
                        {
                            obstaclePosition.X = closestStructure.SimPosition.X;
                        }
                        avoidObstaclePos = obstaclePosition;
                    }
                    else
                    {
                        avoidObstaclePos = Submarine.LastPickedPosition;
                    }
                }
            }
            else
            {
                rayCastTimer -= deltaTime;
            }
            if (!avoidObstaclePos.HasValue)
            {
                return(Vector2.Zero);
            }
            Vector2 diff = avoidObstaclePos.Value - host.SimPosition;
            float   dist = diff.Length();

            if (dist > maxDistance)
            {
                return(Vector2.Zero);
            }
            if (heading.HasValue)
            {
                var f = heading ?? host.Steering;
                // Avoid to left or right depending on the current heading
                Vector2 relativeVector = Vector2.Normalize(diff) - Vector2.Normalize(f);
                var     dir            = relativeVector.X > 0 ? diff.Right() : diff.Left();
                float   factor         = 1.0f - Math.Min(dist / maxDistance, 1);
                return(dir * factor * weight);
            }
            else
            {
                // Doesn't work right because it effectively just slows down or reverses the movement, where as we'd like to go right or left to avoid the target.
                // There's also another issue, which also affects going right or left: the raycast doesn't hit anything if we turn too much -> avoiding doesn't work well.
                // Could probably "remember" the avoidance a bit longer so that the avoid steering is not immedieately disgarded, but kept for a while and reduced gradually?
                return(-diff * (1.0f - dist / maxDistance) * weight);
            }
        }
Пример #18
0
        private WayPoint FindClosest(int dir, bool horizontalSearch, Vector2 tolerance, Body ignoredBody = null, IEnumerable <WayPoint> ignored = null, Func <WayPoint, bool> filter = null)
        {
            if (dir != -1 && dir != 1)
            {
                return(null);
            }

            float    closestDist = 0.0f;
            WayPoint closest     = null;

            foreach (WayPoint wp in WayPointList)
            {
                if (wp.SpawnType != SpawnType.Path || wp == this)
                {
                    continue;
                }

                float xDiff = wp.Position.X - Position.X;
                float yDiff = wp.Position.Y - Position.Y;
                float xDist = Math.Abs(xDiff);
                float yDist = Math.Abs(yDiff);
                if (tolerance.X < xDist)
                {
                    continue;
                }
                if (tolerance.Y < yDist)
                {
                    continue;
                }

                float dist = 0.0f;
                float diff = 0.0f;
                if (horizontalSearch)
                {
                    diff = xDiff;
                    dist = xDist + yDist / 5.0f;
                }
                else
                {
                    diff = yDiff;
                    dist = yDist + xDist / 5.0f;
                    //prefer ladder waypoints when moving vertically
                    if (wp.Ladders != null)
                    {
                        dist *= 0.5f;
                    }
                }

                if (Math.Sign(diff) != dir)
                {
                    continue;
                }
                // Ignore if already linked
                if (linkedTo.Contains(wp))
                {
                    continue;
                }
                if (ignored != null && ignored.Contains(wp))
                {
                    continue;
                }
                if (filter != null && !filter(wp))
                {
                    continue;
                }

                if (closest == null || dist < closestDist)
                {
                    var body = Submarine.CheckVisibility(SimPosition, wp.SimPosition, ignoreLevel: true, ignoreSubs: true, ignoreSensors: false);
                    if (body != null && body != ignoredBody && !(body.UserData is Submarine))
                    {
                        if (body.UserData is Structure || body.FixtureList[0].CollisionCategories.HasFlag(Physics.CollisionWall))
                        {
                            continue;
                        }
                    }

                    closestDist = dist;
                    closest     = wp;
                }
            }

            return(closest);
        }
Пример #19
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);
                        }
                    }
                }
            }
        }
        public override void DragCharacter(Character target, float deltaTime)
        {
            if (target == null)
            {
                return;
            }

            Limb mouthLimb = Array.Find(Limbs, l => l != null && l.MouthPos.HasValue);

            if (mouthLimb == null)
            {
                mouthLimb = GetLimb(LimbType.Head);
            }

            if (mouthLimb == null)
            {
                DebugConsole.ThrowError("Character \"" + character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)");
                return;
            }

            if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
            {
                //stop dragging if there's something between the pull limb and the target
                Vector2 sourceSimPos = mouthLimb.SimPosition;
                Vector2 targetSimPos = target.SimPosition;
                if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
                {
                    targetSimPos -= character.Submarine.SimPosition;
                }
                else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
                {
                    sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
                }
                var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
                if (body != null)
                {
                    character.DeselectCharacter();
                    return;
                }
            }

            Character targetCharacter = target;
            float     eatSpeed        = character.Mass / targetCharacter.Mass * 0.1f;

            eatTimer += deltaTime * eatSpeed;

            Vector2 mouthPos          = GetMouthPosition().Value;
            Vector2 attackSimPosition = character.Submarine == null?ConvertUnits.ToSimUnits(target.WorldPosition) : target.SimPosition;

            Vector2 limbDiff = attackSimPosition - mouthPos;
            float   limbDist = limbDiff.Length();

            if (limbDist < 1.0f)
            {
                //pull the target character to the position of the mouth
                //(+ make the force fluctuate to waggle the character a bit)
                if (CanDrag(target))
                {
                    targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
                    targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation, 20.0f);
                    targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f));
                }

                //pull the character's mouth to the target character (again with a fluctuating force)
                float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f));
                mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);

                character.ApplyStatusEffects(ActionType.OnEating, deltaTime);

                if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f)
                {
                    //apply damage to the target character to get some blood particles flying
                    targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, 0.0f, 20.0f, 0.0f, false);

                    //keep severing joints until there is only one limb left
                    LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints,
                                                                 l => !l.IsSevered && l.CanBeSevered && l.LimbA != null && !l.LimbA.IsSevered && l.LimbB != null && !l.LimbB.IsSevered);
                    if (nonSeveredJoints.Length == 0)
                    {
                        //only one limb left, the character is now full eaten
                        Entity.Spawner?.AddToRemoveQueue(targetCharacter);
                        character.SelectedCharacter = null;
                    }
                    else //sever a random joint
                    {
                        targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]);
                    }
                }
            }
            else
            {
                character.SelectedCharacter = null;
            }
        }
Пример #21
0
        public static void Update(float deltaTime, Character character, Camera cam)
        {
            if (GUI.DisableHUD)
            {
                if (character.Inventory != null && !LockInventory(character))
                {
                    character.Inventory.UpdateSlotInput();
                }

                return;
            }

            if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen)
            {
                if (character.Info != null && !character.ShouldLockHud() && character.SelectedCharacter == null)
                {
                    bool mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && GUI.MouseOn == null;
                    if (mouseOnPortrait && PlayerInput.PrimaryMouseButtonClicked())
                    {
                        CharacterHealth.OpenHealthWindow = character.CharacterHealth;
                    }
                }

                if (character.Inventory != null)
                {
                    if (!LockInventory(character))
                    {
                        character.Inventory.Update(deltaTime, cam);
                    }
                    for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
                    {
                        var item = character.Inventory.Items[i];
                        if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any)
                        {
                            continue;
                        }

                        foreach (ItemComponent ic in item.Components)
                        {
                            if (ic.DrawHudWhenEquipped)
                            {
                                ic.UpdateHUD(character, deltaTime, cam);
                            }
                        }
                    }
                }

                if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
                {
                    if (character.SelectedCharacter.CanInventoryBeAccessed)
                    {
                        character.SelectedCharacter.Inventory.Update(deltaTime, cam);
                    }
                    character.SelectedCharacter.CharacterHealth.UpdateHUD(deltaTime);
                }

                Inventory.UpdateDragging();
            }

            if (focusedItem != null)
            {
                if (character.FocusedItem != null)
                {
                    focusedItemOverlayTimer = Math.Min(focusedItemOverlayTimer + deltaTime, ItemOverlayDelay + 1.0f);
                }
                else
                {
                    focusedItemOverlayTimer = Math.Max(focusedItemOverlayTimer - deltaTime, 0.0f);
                    if (focusedItemOverlayTimer <= 0.0f)
                    {
                        focusedItem            = null;
                        shouldRecreateHudTexts = true;
                    }
                }
            }

            if (brokenItemsCheckTimer > 0.0f)
            {
                brokenItemsCheckTimer -= deltaTime;
            }
            else
            {
                brokenItems.Clear();
                brokenItemsCheckTimer = 1.0f;
                foreach (Item item in Item.ItemList)
                {
                    if (item.Submarine == null || item.Submarine.TeamID != character.TeamID || item.Submarine.Info.IsWreck)
                    {
                        continue;
                    }
                    if (!item.Repairables.Any(r => item.ConditionPercentage <= r.RepairIconThreshold))
                    {
                        continue;
                    }
                    if (Submarine.VisibleEntities != null && !Submarine.VisibleEntities.Contains(item))
                    {
                        continue;
                    }

                    Vector2 diff = item.WorldPosition - character.WorldPosition;
                    if (Submarine.CheckVisibility(character.SimPosition, character.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
                    {
                        brokenItems.Add(item);
                    }
                }
            }
        }
Пример #22
0
        public static void Update(float deltaTime, Character character, Camera cam)
        {
            if (GUI.DisableHUD)
            {
                return;
            }

            if (!character.IsUnconscious && character.Stun <= 0.0f)
            {
                if (character.Inventory != null)
                {
                    if (!character.LockHands && character.Stun < 0.1f)
                    {
                        character.Inventory.Update(deltaTime, cam);
                    }

                    for (int i = 0; i < character.Inventory.Items.Length - 1; i++)
                    {
                        var item = character.Inventory.Items[i];
                        if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any)
                        {
                            continue;
                        }

                        foreach (ItemComponent ic in item.Components)
                        {
                            if (ic.DrawHudWhenEquipped)
                            {
                                ic.UpdateHUD(character, deltaTime, cam);
                            }
                        }
                    }
                }

                if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null)
                {
                    if (character.SelectedCharacter.CanInventoryBeAccessed)
                    {
                        character.SelectedCharacter.Inventory.Update(deltaTime, cam);
                    }
                    character.SelectedCharacter.CharacterHealth.UpdateHUD(deltaTime);
                }

                Inventory.UpdateDragging();
            }

            if (focusedItem != null)
            {
                if (character.FocusedItem != null)
                {
                    focusedItemOverlayTimer = Math.Min(focusedItemOverlayTimer + deltaTime, ItemOverlayDelay + 1.0f);
                }
                else
                {
                    focusedItemOverlayTimer = Math.Max(focusedItemOverlayTimer - deltaTime, 0.0f);
                    if (focusedItemOverlayTimer <= 0.0f)
                    {
                        focusedItem = null;
                    }
                }
            }

            if (brokenItemsCheckTimer > 0.0f)
            {
                brokenItemsCheckTimer -= deltaTime;
            }
            else
            {
                brokenItems.Clear();
                brokenItemsCheckTimer = 1.0f;
                foreach (Item item in Item.ItemList)
                {
                    if (!item.Repairables.Any(r => item.Condition < r.ShowRepairUIThreshold))
                    {
                        continue;
                    }
                    if (!Submarine.VisibleEntities.Contains(item))
                    {
                        continue;
                    }

                    Vector2 diff = item.WorldPosition - character.WorldPosition;
                    if (Submarine.CheckVisibility(character.SimPosition, character.SimPosition + ConvertUnits.ToSimUnits(diff)) == null)
                    {
                        brokenItems.Add(item);
                    }
                }
            }
        }
Пример #23
0
        //goes through all the AItargets, evaluates how preferable it is to attack the target,
        //whether the Character can see/hear the target and chooses the most preferable target within
        //sight/hearing range
        public void UpdateTargets(Character character)
        {
            var prevAiTarget = selectedAiTarget;

            selectedAiTarget     = null;
            selectedTargetMemory = null;
            targetValue          = 0.0f;

            UpdateTargetMemories();

            foreach (AITarget target in AITarget.List)
            {
                if (Level.Loaded != null && target.WorldPosition.Y > Level.Loaded.Size.Y)
                {
                    continue;
                }

                float valueModifier = 0.0f;
                float dist          = 0.0f;


                Character targetCharacter = target.Entity as Character;

                //ignore the aitarget if it is the Character itself
                if (targetCharacter == character)
                {
                    continue;
                }

                if (targetCharacter != null)
                {
                    if (targetCharacter.IsDead)
                    {
                        if (eatDeadPriority == 0.0f)
                        {
                            continue;
                        }
                        valueModifier = eatDeadPriority;
                    }
                    else if (targetCharacter.SpeciesName == "human")
                    {
                        if (attackHumans == 0.0f)
                        {
                            continue;
                        }
                        valueModifier = attackHumans;
                    }
                    else
                    {
                        EnemyAIController enemy = targetCharacter.AIController as EnemyAIController;
                        if (enemy != null)
                        {
                            if (enemy.combatStrength > combatStrength)
                            {
                                valueModifier = attackStronger;
                            }
                            else if (enemy.combatStrength < combatStrength)
                            {
                                valueModifier = attackWeaker;
                            }
                            else
                            {
                                continue;
                            }
                        }
                    }
                }
                else if (target.Entity != null && attackRooms != 0.0f)
                {
                    IDamageable targetDamageable = target.Entity as IDamageable;
                    if (targetDamageable != null && targetDamageable.Health <= 0.0f)
                    {
                        continue;
                    }

                    //skip the target if it's a room and the character is already inside a sub
                    if (character.AnimController.CurrentHull != null && target.Entity is Hull)
                    {
                        continue;
                    }

                    valueModifier = attackRooms;
                }

                if (valueModifier == 0.0f)
                {
                    continue;
                }

                dist = Vector2.Distance(character.WorldPosition, target.WorldPosition);

                //if the target has been within range earlier, the character will notice it more easily
                //(i.e. remember where the target was)
                if (targetMemories.ContainsKey(target))
                {
                    dist *= 0.5f;
                }

                //ignore target if it's too far to see or hear
                if (dist > target.SightRange * sight && dist > target.SoundRange * hearing)
                {
                    continue;
                }

                AITargetMemory targetMemory = FindTargetMemory(target);
                valueModifier = valueModifier * targetMemory.Priority / dist;

                if (Math.Abs(valueModifier) > Math.Abs(targetValue))
                {
                    Vector2 rayStart = character.AnimController.Limbs[0].SimPosition;
                    Vector2 rayEnd   = target.SimPosition;

                    if (target.Entity.Submarine != null && character.Submarine == null)
                    {
                        rayStart -= ConvertUnits.ToSimUnits(target.Entity.Submarine.Position);
                    }

                    Body      closestBody      = Submarine.CheckVisibility(rayStart, rayEnd);
                    Structure closestStructure = (closestBody == null) ? null : closestBody.UserData as Structure;

                    if (selectedAiTarget == null || Math.Abs(valueModifier) > Math.Abs(targetValue))
                    {
                        selectedAiTarget     = target;
                        selectedTargetMemory = targetMemory;

                        targetValue = valueModifier;
                    }
                }
            }

            if (selectedAiTarget != prevAiTarget)
            {
                wallAttackPos = Vector2.Zero;
            }
        }
Пример #24
0
        public static void ApplyExplosionForces(Vector2 worldPosition, Attack attack, float force)
        {
            if (attack.Range <= 0.0f)
            {
                return;
            }

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

                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 - FarseerPhysics.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);

                    c.AddDamage(limb.WorldPosition, DamageType.None,
                                attack.GetDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
                                attack.GetBleedingDamage(1.0f) / c.AnimController.Limbs.Length * distFactor,
                                attack.Stun * distFactor,
                                false);

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

                //sever joints
                if (c.IsDead && attack.SeverLimbsProbability > 0.0f)
                {
                    foreach (Limb limb in c.AnimController.Limbs)
                    {
                        if (!distFactors.ContainsKey(limb))
                        {
                            continue;
                        }

                        foreach (LimbJoint joint in c.AnimController.LimbJoints)
                        {
                            if (joint.IsSevered || (joint.LimbA != limb && joint.LimbB != limb))
                            {
                                continue;
                            }

                            if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb])
                            {
                                c.AnimController.SeverLimbJoint(joint);
                            }
                        }
                    }
                }
            }
        }