Пример #1
0
        private void OperateRepairTool(float deltaTime)
        {
            character.CursorPosition = Item.WorldPosition;
            if (character.Submarine != null)
            {
                character.CursorPosition -= character.Submarine.Position;
            }
            if (repairTool.Item.RequireAimToUse)
            {
                character.SetInput(InputType.Aim, false, true);
            }
            Vector2 fromToolToTarget = Item.Position - repairTool.Item.Position;

            if (fromToolToTarget.LengthSquared() < MathUtils.Pow(repairTool.Range / 2, 2))
            {
                // Too close -> steer away
                character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - Item.SimPosition) / 2);
            }
            else
            {
                character.AIController.SteeringManager.Reset();
            }
            if (VectorExtensions.Angle(VectorExtensions.Forward(repairTool.Item.body.TransformedRotation), fromToolToTarget) < MathHelper.PiOver4)
            {
                repairTool.Use(deltaTime, character);
            }
        }
Пример #2
0
        public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
        {
            if (armorSector == Vector2.Zero)
            {
                return(false);
            }
            float rotation   = body.TransformedRotation;
            float offset     = (MathHelper.PiOver2 - GetArmorSectorRotationOffset(armorSector)) * Dir;
            float hitAngle   = VectorExtensions.Angle(VectorExtensions.Forward(rotation + offset), SimPosition - simPosition);
            float sectorSize = GetArmorSectorSize(armorSector);

            return(hitAngle < sectorSize / 2);
        }
 private void Attack(float deltaTime)
 {
     character.CursorPosition = Enemy.Position;
     if (Weapon.RequireAimToUse)
     {
         character.SetInput(InputType.Aim, false, true);
     }
     if (WeaponComponent is MeleeWeapon meleeWeapon)
     {
         if (Vector2.DistanceSquared(character.Position, Enemy.Position) <= meleeWeapon.Range * meleeWeapon.Range)
         {
             character.SetInput(InputType.Shoot, false, true);
             Weapon.Use(deltaTime, character);
         }
     }
     else
     {
         if (WeaponComponent is RepairTool repairTool)
         {
             if (Vector2.DistanceSquared(character.Position, Enemy.Position) > repairTool.Range * repairTool.Range)
             {
                 return;
             }
         }
         if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - character.Position) < MathHelper.PiOver4)
         {
             if (myBodies == null)
             {
                 myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
             }
             var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies);
             if (pickedBody != null)
             {
                 Character target = null;
                 if (pickedBody.UserData is Character c)
                 {
                     target = c;
                 }
                 else if (pickedBody.UserData is Limb limb)
                 {
                     target = limb.character;
                 }
                 if (target != null && target == Enemy)
                 {
                     character.SetInput(InputType.Shoot, false, true);
                     Weapon.Use(deltaTime, character);
                 }
             }
         }
     }
 }
Пример #4
0
        public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
        {
            if (armorSector == Vector2.Zero)
            {
                return(false);
            }
            //sector 360 degrees or more -> always hits
            if (Math.Abs(armorSector.Y - armorSector.X) >= MathHelper.TwoPi)
            {
                return(true);
            }
            float rotation   = body.TransformedRotation;
            float offset     = (MathHelper.PiOver2 - GetArmorSectorRotationOffset(armorSector)) * Dir;
            float hitAngle   = VectorExtensions.Angle(VectorExtensions.Forward(rotation + offset), SimPosition - simPosition);
            float sectorSize = GetArmorSectorSize(armorSector);

            return(hitAngle < sectorSize / 2);
        }
Пример #5
0
        private void Attack(float deltaTime)
        {
            character.CursorPosition = Enemy.Position;
            if (!character.CanSeeCharacter(Enemy))
            {
                return;
            }
            if (Weapon.RequireAimToUse)
            {
                bool isOperatingButtons = false;
                if (SteeringManager == PathSteering)
                {
                    var door = PathSteering.CurrentPath?.CurrentNode?.ConnectedDoor;
                    if (door != null && !door.IsOpen && !door.IsBroken)
                    {
                        isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents <Controller>(true).Any();
                    }
                }
                if (!isOperatingButtons)
                {
                    character.SetInput(InputType.Aim, false, true);
                }
            }
            bool isFacing = character.AnimController.Dir > 0 && Enemy.WorldPosition.X > character.WorldPosition.X || character.AnimController.Dir < 0 && Enemy.WorldPosition.X < character.WorldPosition.X;

            if (!isFacing)
            {
                aimTimer = Rand.Range(1f, 1.5f);
            }
            if (aimTimer > 0)
            {
                aimTimer -= deltaTime;
                return;
            }
            if (WeaponComponent is MeleeWeapon meleeWeapon)
            {
                if (Vector2.DistanceSquared(character.Position, Enemy.Position) <= meleeWeapon.Range * meleeWeapon.Range)
                {
                    character.SetInput(InputType.Shoot, false, true);
                    Weapon.Use(deltaTime, character);
                }
            }
            else
            {
                if (WeaponComponent is RepairTool repairTool)
                {
                    if (Vector2.DistanceSquared(character.Position, Enemy.Position) > repairTool.Range * repairTool.Range)
                    {
                        return;
                    }
                }
                if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4)
                {
                    if (myBodies == null)
                    {
                        myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
                    }

                    var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
                    var pickedBody          = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
                    if (pickedBody != null)
                    {
                        Character target = null;
                        if (pickedBody.UserData is Character c)
                        {
                            target = c;
                        }
                        else if (pickedBody.UserData is Limb limb)
                        {
                            target = limb.character;
                        }
                        if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target)))
                        {
                            character.SetInput(InputType.Shoot, false, true);
                            Weapon.Use(deltaTime, character);
                            float reloadTime = 0;
                            if (WeaponComponent is RangedWeapon rangedWeapon)
                            {
                                reloadTime = rangedWeapon.Reload;
                            }
                            if (WeaponComponent is MeleeWeapon mw)
                            {
                                reloadTime = mw.Reload;
                            }
                            aimTimer = reloadTime * Rand.Range(1f, 1.5f);
                        }
                    }
                }
            }
        }
Пример #6
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            Gap leak = objective.OperateTarget as Gap;

            if (leak == null)
            {
                return(true);
            }

            Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
            float   dist           = fromItemToLeak.Length();

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > Range * 3.0f)
            {
                return(true);
            }

            // TODO: use the collider size?
            if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
                Math.Abs(fromItemToLeak.X) < 100.0f && fromItemToLeak.Y < 0.0f && fromItemToLeak.Y > -150.0f)
            {
                ((HumanoidAnimController)character.AnimController).Crouching = true;
            }

            //steer closer if almost in range
            if (dist > Range)
            {
                Vector2 standPos = new Vector2(Math.Sign(-fromItemToLeak.X), Math.Sign(-fromItemToLeak.Y)) / 2;
                if (!character.AnimController.InWater)
                {
                    if (leak.IsHorizontal)
                    {
                        standPos.X *= 2;
                        standPos.Y  = 0;
                    }
                    else
                    {
                        standPos.X = 0;
                    }
                }
                if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
                {
                    if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
                    {
                        Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
                        character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
                    }
                    else
                    {
                        character.AIController.SteeringManager.SteeringSeek(standPos);
                    }
                }
                else
                {
                    character.AIController.SteeringManager.SteeringSeek(standPos);
                }
            }
            else
            {
                if (dist < Range / 2)
                {
                    // Too close -> steer away
                    character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
                }
                else if (dist <= Range)
                {
                    // In range
                    character.AIController.SteeringManager.Reset();
                }
                else
                {
                    return(false);
                }
            }
            sinTime += deltaTime;
            character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
            if (item.RequireAimToUse)
            {
                bool isOperatingButtons = false;
                if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
                {
                    var door = indoorSteering.CurrentPath?.CurrentNode?.ConnectedDoor;
                    if (door != null && !door.IsOpen)
                    {
                        isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents <Controller>(true).Any();
                    }
                }
                if (!isOperatingButtons)
                {
                    character.SetInput(InputType.Aim, false, true);
                }
            }
            // Press the trigger only when the tool is approximately facing the target.
            var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);

            if (angle < MathHelper.PiOver4)
            {
                character.SetInput(InputType.Shoot, false, true);
                Use(deltaTime, character);
            }

            bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
                             (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);

            if (leakFixed && leak.FlowTargetHull != null)
            {
                sinTime = 0;
                if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
                }
                else
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f);
                }
            }

            return(leakFixed);
        }
Пример #7
0
        void UpdateSineAnim(float deltaTime)
        {
            if (CurrentSwimParams == null)
            {
                return;
            }
            movement = TargetMovement;

            if (movement.LengthSquared() > 0.00001f)
            {
                float t = 0.5f;
                if (CurrentSwimParams.RotateTowardsMovement && VectorExtensions.Angle(VectorExtensions.Forward(Collider.Rotation + MathHelper.PiOver2), movement) > MathHelper.PiOver2)
                {
                    // Reduce the linear movement speed when not facing the movement direction
                    t /= 5;
                }
                Collider.LinearVelocity = Vector2.Lerp(Collider.LinearVelocity, movement, t);
            }

            //limbs are disabled when simple physics is enabled, no need to move them
            if (SimplePhysicsEnabled)
            {
                return;
            }
            var mainLimb = MainLimb;

            mainLimb.PullJointEnabled = true;
            //mainLimb.PullJointWorldAnchorB = Collider.SimPosition;

            if (movement.LengthSquared() < 0.00001f)
            {
                WalkPos = MathHelper.SmoothStep(WalkPos, MathHelper.PiOver2, deltaTime * 5);
                mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                return;
            }

            Vector2 transformedMovement = reverse ? -movement : movement;
            float   movementAngle       = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
            float   mainLimbAngle       = 0;

            if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
            {
                mainLimbAngle = TorsoAngle.Value;
            }
            else if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
            {
                mainLimbAngle = HeadAngle.Value;
            }
            mainLimbAngle *= Dir;
            while (mainLimb.Rotation - (movementAngle + mainLimbAngle) > MathHelper.Pi)
            {
                movementAngle += MathHelper.TwoPi;
            }
            while (mainLimb.Rotation - (movementAngle + mainLimbAngle) < -MathHelper.Pi)
            {
                movementAngle -= MathHelper.TwoPi;
            }

            if (CurrentSwimParams.RotateTowardsMovement)
            {
                Collider.SmoothRotate(movementAngle, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
                if (TorsoAngle.HasValue)
                {
                    Limb torso = GetLimb(LimbType.Torso);
                    if (torso != null)
                    {
                        SmoothRotateWithoutWrapping(torso, movementAngle + TorsoAngle.Value * Dir, mainLimb, TorsoTorque);
                    }
                }
                if (HeadAngle.HasValue)
                {
                    Limb head = GetLimb(LimbType.Head);
                    if (head != null)
                    {
                        SmoothRotateWithoutWrapping(head, movementAngle + HeadAngle.Value * Dir, mainLimb, HeadTorque);
                    }
                }
                if (TailAngle.HasValue)
                {
                    Limb tail = GetLimb(LimbType.Tail);
                    if (tail != null)
                    {
                        float?mainLimbTargetAngle = null;
                        if (mainLimb.type == LimbType.Torso)
                        {
                            mainLimbTargetAngle = TorsoAngle;
                        }
                        else if (mainLimb.type == LimbType.Head)
                        {
                            mainLimbTargetAngle = HeadAngle;
                        }
                        float torque        = TailTorque;
                        float maxMultiplier = CurrentSwimParams.TailTorqueMultiplier;
                        if (mainLimbTargetAngle.HasValue && maxMultiplier > 1)
                        {
                            float diff   = Math.Abs(mainLimb.Rotation - tail.Rotation);
                            float offset = Math.Abs(mainLimbTargetAngle.Value - TailAngle.Value);
                            torque *= MathHelper.Lerp(1, maxMultiplier, MathUtils.InverseLerp(0, MathHelper.PiOver2, diff - offset));
                        }
                        SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, mainLimb, torque);
                    }
                }
            }
            else
            {
                movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
                if (reverse)
                {
                    movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
                }
                if (mainLimb.type == LimbType.Head && HeadAngle.HasValue)
                {
                    Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
                }
                else if (mainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
                {
                    Collider.SmoothRotate(TorsoAngle.Value * Dir, CurrentSwimParams.SteerTorque * character.SpeedMultiplier);
                }
                if (TorsoAngle.HasValue)
                {
                    Limb torso = GetLimb(LimbType.Torso);
                    torso?.body.SmoothRotate(TorsoAngle.Value * Dir, TorsoTorque);
                }
                if (HeadAngle.HasValue)
                {
                    Limb head = GetLimb(LimbType.Head);
                    head?.body.SmoothRotate(HeadAngle.Value * Dir, HeadTorque);
                }
                if (TailAngle.HasValue)
                {
                    Limb tail = GetLimb(LimbType.Tail);
                    tail?.body.SmoothRotate(TailAngle.Value * Dir, TailTorque);
                }
            }

            var waveLength    = Math.Abs(CurrentSwimParams.WaveLength * RagdollParams.JointScale);
            var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude * character.SpeedMultiplier);

            if (waveLength > 0 && waveAmplitude > 0)
            {
                WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
                WalkPos  = MathUtils.WrapAngleTwoPi(WalkPos);
            }

            foreach (var limb in Limbs)
            {
                if (limb.IsSevered)
                {
                    continue;
                }
                if (Math.Abs(limb.Params.ConstantTorque) > 0)
                {
                    limb.body.SmoothRotate(movementAngle + MathHelper.ToRadians(limb.Params.ConstantAngle) * Dir, limb.Params.ConstantTorque, wrapAngle: true);
                }
                switch (limb.type)
                {
                case LimbType.LeftFoot:
                case LimbType.RightFoot:
                    if (CurrentSwimParams.FootAnglesInRadians.ContainsKey(limb.Params.ID))
                    {
                        SmoothRotateWithoutWrapping(limb, movementAngle + CurrentSwimParams.FootAnglesInRadians[limb.Params.ID] * Dir, mainLimb, FootTorque);
                    }
                    break;

                case LimbType.Tail:
                    if (waveLength > 0 && waveAmplitude > 0)
                    {
                        float waveRotation = (float)Math.Sin(WalkPos);
                        limb.body.ApplyTorque(waveRotation * limb.Mass * waveAmplitude);
                    }
                    break;
                }
            }

            for (int i = 0; i < Limbs.Length; i++)
            {
                var limb = Limbs[i];
                if (limb.IsSevered)
                {
                    continue;
                }
                if (limb.SteerForce <= 0.0f)
                {
                    continue;
                }
                if (!Collider.PhysEnabled)
                {
                    continue;
                }
                Vector2 pullPos = limb.PullJointWorldAnchorA;
                limb.body.ApplyForce(movement * limb.SteerForce * limb.Mass * Math.Max(character.SpeedMultiplier, 1), pullPos);
            }

            Vector2 mainLimbDiff = mainLimb.PullJointWorldAnchorB - mainLimb.SimPosition;

            if (CurrentSwimParams.UseSineMovement)
            {
                mainLimb.PullJointWorldAnchorB = Vector2.SmoothStep(
                    mainLimb.PullJointWorldAnchorB,
                    Collider.SimPosition,
                    mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : (float)Math.Abs(Math.Sin(WalkPos)));
            }
            else
            {
                //mainLimb.PullJointWorldAnchorB = Collider.SimPosition;
                mainLimb.PullJointWorldAnchorB = Vector2.Lerp(
                    mainLimb.PullJointWorldAnchorB,
                    Collider.SimPosition,
                    mainLimbDiff.LengthSquared() > 10.0f ? 1.0f : 0.5f);
            }

            floorY = Limbs[0].SimPosition.Y;
        }
Пример #8
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            if (!(objective.OperateTarget is Gap leak))
            {
                return(true);
            }
            if (leak.Submarine == null)
            {
                return(true);
            }
            Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition;
            float   dist  = fromCharacterToLeak.Length();
            float   reach = Range + ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength);

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > reach * 2)
            {
                return(true);
            }
            character.AIController.SteeringManager.Reset();
            //steer closer if almost in range
            if (dist > reach)
            {
                if (character.AnimController.InWater)
                {
                    if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
                    {
                        // Swimming inside the sub
                        if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
                        {
                            Vector2 dir = Vector2.Normalize(fromCharacterToLeak);
                            character.AIController.SteeringManager.SteeringManual(deltaTime, dir);
                        }
                        else
                        {
                            character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
                        }
                    }
                    else
                    {
                        // Swimming outside the sub
                        character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak));
                    }
                }
                else
                {
                    // TODO: use the collider size?
                    if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
                        Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f)
                    {
                        ((HumanoidAnimController)character.AnimController).Crouching = true;
                    }
                    Vector2 standPos = new Vector2(Math.Sign(-fromCharacterToLeak.X), Math.Sign(-fromCharacterToLeak.Y)) / 2;
                    if (leak.IsHorizontal)
                    {
                        standPos.X *= 2;
                        standPos.Y  = 0;
                    }
                    else
                    {
                        standPos.X = 0;
                    }
                    character.AIController.SteeringManager.SteeringSeek(standPos);
                }
            }
            else
            {
                if (dist < reach / 2)
                {
                    // Too close -> steer away
                    character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition));
                }
                else if (dist <= reach)
                {
                    // In range
                    character.CursorPosition  = leak.Position;
                    character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2);
                    if (character.AnimController.InWater)
                    {
                        var torso = character.AnimController.GetLimb(LimbType.Torso);
                        // Turn facing the target when not moving (handled in the animcontroller if not moving)
                        Vector2 mousePos    = ConvertUnits.ToSimUnits(character.CursorPosition);
                        Vector2 diff        = (mousePos - torso.SimPosition) * character.AnimController.Dir;
                        float   newRotation = MathUtils.VectorToAngle(diff);
                        character.AnimController.Collider.SmoothRotate(newRotation, 5.0f);

                        if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4)
                        {
                            // Swim past
                            Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX;
                            moveDir *= character.AnimController.Dir;
                            character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir);
                        }
                    }
                }
            }
            if (item.RequireAimToUse)
            {
                character.SetInput(InputType.Aim, false, true);
                sinTime += deltaTime * 5;
            }
            // Press the trigger only when the tool is approximately facing the target.
            Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
            var     angle          = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);

            if (angle < MathHelper.PiOver4)
            {
                // Check that we don't hit any friendlies
                if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit =>
                {
                    if (hit.UserData is Character c)
                    {
                        if (c == character)
                        {
                            return(false);
                        }
                        return(HumanAIController.IsFriendly(character, c));
                    }
                    return(false);
                }))
                {
                    character.SetInput(InputType.Shoot, false, true);
                    Use(deltaTime, character);
                }
            }

            bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
                             (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);

            if (leakFixed && leak.FlowTargetHull != null)
            {
                if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f);
                }
                else
                {
                    character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f);
                }
            }

            return(leakFixed);
        }
Пример #9
0
        public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
        {
            Gap leak = objective.OperateTarget as Gap;

            if (leak == null)
            {
                return(true);
            }

            Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition;
            float   dist           = fromItemToLeak.Length();

            //too far away -> consider this done and hope the AI is smart enough to move closer
            if (dist > Range * 5.0f)
            {
                return(true);
            }

            // TODO: use the collider size?
            if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController &&
                Math.Abs(fromItemToLeak.X) < 100.0f && fromItemToLeak.Y < 0.0f && fromItemToLeak.Y > -150.0f)
            {
                ((HumanoidAnimController)character.AnimController).Crouching = true;
            }

            //steer closer if almost in range
            if (dist > Range)
            {
                Vector2 standPos = leak.IsHorizontal ? new Vector2(Math.Sign(-fromItemToLeak.X), 0.0f) : new Vector2(0.0f, Math.Sign(-fromItemToLeak.Y) * 0.5f);
                standPos = leak.WorldPosition + standPos * Range;
                Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
                character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
            }
            else
            {
                if (dist < Range / 2)
                {
                    // Too close -> steer away
                    character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
                }
                else
                {
                    character.AIController.SteeringManager.Reset();
                }
            }

            sinTime += deltaTime;
            character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
            if (item.RequireAimToUse)
            {
                character.SetInput(InputType.Aim, false, true);
            }

            // Press the trigger only when the tool is approximately facing the target.
            // If the character is climbing, ignore the check, because we cannot aim while climbing.
            if (VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak) < MathHelper.PiOver4)
            {
                Use(deltaTime, character);
            }
            else
            {
                sinTime -= deltaTime * 2;
            }

            bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
                             (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);

            if (leakFixed && leak.FlowTargetHull != null)
            {
                sinTime = 0;
                if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
                {
                    character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f);
                }
                else
                {
                    character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leakfixed", 10.0f);
                }
            }

            return(leakFixed);
        }
Пример #10
0
        private void Attack(float deltaTime)
        {
            character.CursorPosition = Enemy.Position;
            visibilityCheckTimer    -= deltaTime;
            if (visibilityCheckTimer <= 0.0f)
            {
                canSeeTarget         = character.CanSeeTarget(Enemy);
                visibilityCheckTimer = visibilityCheckInterval;
            }
            if (!canSeeTarget)
            {
                return;
            }
            if (Weapon.RequireAimToUse)
            {
                character.SetInput(InputType.Aim, false, true);
            }
            hasAimed = true;
            if (holdFireTimer > 0)
            {
                holdFireTimer -= deltaTime;
                return;
            }
            if (aimTimer > 0)
            {
                aimTimer -= deltaTime;
                return;
            }
            if (Mode == CombatMode.Arrest && isLethalWeapon && Enemy.Stun > 0)
            {
                return;
            }
            if (holdFireCondition != null && holdFireCondition())
            {
                return;
            }
            float sqrDist = Vector2.DistanceSquared(character.Position, Enemy.Position);

            if (!character.IsFacing(Enemy.WorldPosition))
            {
                aimTimer = Rand.Range(1f, 1.5f);
                return;
            }
            if (WeaponComponent is MeleeWeapon meleeWeapon)
            {
                float sqrRange = meleeWeapon.Range * meleeWeapon.Range;
                if (character.AnimController.InWater)
                {
                    if (sqrDist > sqrRange)
                    {
                        return;
                    }
                }
                else
                {
                    // It's possible that the center point of the creature is out of reach, but we could still hit the character.
                    float xDiff = Math.Abs(Enemy.WorldPosition.X - character.WorldPosition.X);
                    if (xDiff > meleeWeapon.Range)
                    {
                        return;
                    }
                    float yDiff = Math.Abs(Enemy.WorldPosition.Y - character.WorldPosition.Y);
                    if (yDiff > Math.Max(meleeWeapon.Range, 100))
                    {
                        return;
                    }
                    if (Enemy.WorldPosition.Y < character.WorldPosition.Y && yDiff > 25)
                    {
                        // The target is probably knocked down? -> try to reach it by crouching.
                        HumanAIController.AnimController.Crouching = true;
                    }
                }
                character.SetInput(InputType.Shoot, false, true);
                Weapon.Use(deltaTime, character);
            }
            else
            {
                if (WeaponComponent is RepairTool repairTool)
                {
                    if (sqrDist > repairTool.Range * repairTool.Range)
                    {
                        return;
                    }
                }
                if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4)
                {
                    if (myBodies == null)
                    {
                        myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
                    }
                    var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall | Physics.CollisionLevel;
                    var pickedBody          = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
                    if (pickedBody != null)
                    {
                        Character target = null;
                        if (pickedBody.UserData is Character c)
                        {
                            target = c;
                        }
                        else if (pickedBody.UserData is Limb limb)
                        {
                            target = limb.character;
                        }
                        if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target)))
                        {
                            character.SetInput(InputType.Shoot, false, true);
                            Weapon.Use(deltaTime, character);
                            float reloadTime = 0;
                            if (WeaponComponent is RangedWeapon rangedWeapon)
                            {
                                reloadTime = rangedWeapon.Reload;
                            }
                            if (WeaponComponent is MeleeWeapon mw)
                            {
                                reloadTime = mw.Reload;
                            }
                            aimTimer = reloadTime * Rand.Range(1f, 1.5f);
                        }
                    }
                }
            }
        }