private IEnumerable <object> DoInitialCameraTransition()
        {
            while (GameMain.Instance.LoadingScreenOpen)
            {
                yield return(CoroutineStatus.Running);
            }
            Character prevControlled = Character.Controlled;

            if (prevControlled?.AIController != null)
            {
                prevControlled.AIController.Enabled = false;
            }
            Character.Controlled = null;
            if (prevControlled != null)
            {
                prevControlled.ClearInputs();
            }

            GUI.DisableHUD = true;
            while (GameMain.Instance.LoadingScreenOpen)
            {
                yield return(CoroutineStatus.Running);
            }

            if (IsFirstRound || showCampaignResetText)
            {
                overlayColor     = Color.LightGray;
                overlaySprite    = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
                overlayTextColor = Color.Transparent;
                overlayText      = TextManager.GetWithVariables(showCampaignResetText ? "campaignend4" : "campaignstart",
                                                                new string[] { "xxxx", "yyyy" },
                                                                new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
                string pressAnyKeyText = TextManager.Get("pressanykey");
                float  fadeInDuration  = 2.0f;
                float  textDuration    = 10.0f;
                float  timer           = 0.0f;
                while (true)
                {
                    if (timer > fadeInDuration)
                    {
                        overlayTextBottom = pressAnyKeyText;
                        if (PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.PrimaryMouseButtonClicked())
                        {
                            break;
                        }
                    }
                    overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
                    timer            = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
                    yield return(CoroutineStatus.Running);
                }
                var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
                                                      null, null,
                                                      fadeOut: false,
                                                      duration: 5,
                                                      startZoom: 1.5f, endZoom: 1.0f)
                {
                    AllowInterrupt             = true,
                    RemoveControlFromCharacter = false
                };
                fadeInDuration   = 1.0f;
                timer            = 0.0f;
                overlayTextColor = Color.Transparent;
                overlayText      = "";
                while (timer < fadeInDuration)
                {
                    overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
                    timer       += CoroutineManager.DeltaTime;
                    yield return(CoroutineStatus.Running);
                }
                overlayColor = Color.Transparent;
                while (transition.Running)
                {
                    yield return(CoroutineStatus.Running);
                }
                showCampaignResetText = false;
            }
            else
            {
                ISpatialEntity transitionTarget;
                if (prevControlled != null)
                {
                    transitionTarget = prevControlled;
                }
                else
                {
                    transitionTarget = Submarine.MainSub;
                }

                var transition = new CameraTransition(transitionTarget, GameMain.GameScreen.Cam,
                                                      null, null,
                                                      fadeOut: false,
                                                      duration: 5,
                                                      startZoom: 0.5f, endZoom: 1.0f)
                {
                    AllowInterrupt             = true,
                    RemoveControlFromCharacter = false
                };
                while (transition.Running)
                {
                    yield return(CoroutineStatus.Running);
                }
            }

            if (prevControlled != null)
            {
                prevControlled.SelectedConstruction = null;
                if (prevControlled.AIController != null)
                {
                    prevControlled.AIController.Enabled = true;
                }
            }

            if (prevControlled != null)
            {
                Character.Controlled = prevControlled;
            }
            GUI.DisableHUD = false;
            yield return(CoroutineStatus.Success);
        }
        private IEnumerable <object> DoInitialCameraTransition()
        {
            while (GameMain.Instance.LoadingScreenOpen)
            {
                yield return(CoroutineStatus.Running);
            }

            if (GameMain.Client.LateCampaignJoin)
            {
                GameMain.Client.LateCampaignJoin = false;
                yield return(CoroutineStatus.Success);
            }

            Character prevControlled = Character.Controlled;

            if (prevControlled?.AIController != null)
            {
                prevControlled.AIController.Enabled = false;
            }
            GUI.DisableHUD = true;
            if (IsFirstRound)
            {
                Character.Controlled = null;

                if (prevControlled != null)
                {
                    prevControlled.ClearInputs();
                }

                overlayColor     = Color.LightGray;
                overlaySprite    = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
                overlayTextColor = Color.Transparent;
                overlayText      = TextManager.GetWithVariables("campaignstart",
                                                                new string[] { "xxxx", "yyyy" },
                                                                new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
                float fadeInDuration = 1.0f;
                float textDuration   = 10.0f;
                float timer          = 0.0f;
                while (timer < textDuration)
                {
                    // Try to grab the controlled here to prevent inputs, assigned late on multiplayer
                    if (Character.Controlled != null)
                    {
                        prevControlled       = Character.Controlled;
                        Character.Controlled = null;
                        prevControlled.ClearInputs();
                    }
                    GameMain.GameScreen.Cam.Freeze = true;
                    overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
                    timer            = Math.Min(timer + CoroutineManager.DeltaTime, textDuration);
                    yield return(CoroutineStatus.Running);
                }
                var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
                                                      null, null,
                                                      fadeOut: false,
                                                      duration: 5,
                                                      startZoom: 1.5f, endZoom: 1.0f)
                {
                    AllowInterrupt             = true,
                    RemoveControlFromCharacter = false
                };
                fadeInDuration   = 1.0f;
                timer            = 0.0f;
                overlayTextColor = Color.Transparent;
                overlayText      = "";
                while (timer < fadeInDuration)
                {
                    overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
                    timer       += CoroutineManager.DeltaTime;
                    yield return(CoroutineStatus.Running);
                }
                overlayColor = Color.Transparent;
                while (transition.Running)
                {
                    yield return(CoroutineStatus.Running);
                }

                if (prevControlled != null)
                {
                    Character.Controlled = prevControlled;
                }
            }
            else
            {
                var transition = new CameraTransition(Submarine.MainSub, GameMain.GameScreen.Cam,
                                                      null, null,
                                                      fadeOut: false,
                                                      duration: 5,
                                                      startZoom: 0.5f, endZoom: 1.0f)
                {
                    AllowInterrupt             = true,
                    RemoveControlFromCharacter = true
                };
                while (transition.Running)
                {
                    yield return(CoroutineStatus.Running);
                }
            }

            if (prevControlled != null)
            {
                prevControlled.SelectedConstruction = null;
                if (prevControlled.AIController != null)
                {
                    prevControlled.AIController.Enabled = true;
                }
            }
            GUI.DisableHUD = false;
            yield return(CoroutineStatus.Success);
        }
        public override void Update(float deltaTime)
        {
            if (DisableCrewAI || Character.IsUnconscious)
            {
                return;
            }

            float maxDistanceToSub = 3000;

            if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null &&
                Vector2.DistanceSquared(Character.WorldPosition, SelectedAiTarget.Entity.Submarine.WorldPosition) < maxDistanceToSub * maxDistanceToSub)
            {
                if (steeringManager != insideSteering)
                {
                    insideSteering.Reset();
                }
                steeringManager = insideSteering;
            }
            else
            {
                if (steeringManager != outsideSteering)
                {
                    outsideSteering.Reset();
                }
                steeringManager = outsideSteering;
            }

            AnimController.Crouching = shouldCrouch;
            CheckCrouching(deltaTime);
            Character.ClearInputs();

            if (hullVisibilityTimer > 0)
            {
                hullVisibilityTimer--;
            }
            else
            {
                hullVisibilityTimer = hullVisibilityInterval;
                VisibleHulls        = Character.GetVisibleHulls();
            }

            objectiveManager.UpdateObjectives(deltaTime);
            if (sortTimer > 0.0f)
            {
                sortTimer -= deltaTime;
            }
            else
            {
                objectiveManager.SortObjectives();
                sortTimer = sortObjectiveInterval;
            }
            if (reactTimer > 0.0f)
            {
                reactTimer -= deltaTime;
            }
            else
            {
                if (Character.CurrentHull != null)
                {
                    VisibleHulls.ForEach(h => PropagateHullSafety(Character, h));
                }
                if (Character.SpeechImpediment < 100.0f)
                {
                    ReportProblems();
                    UpdateSpeaking();
                }
                reactTimer = reactionTime * Rand.Range(0.75f, 1.25f);
            }

            if (objectiveManager.CurrentObjective == null)
            {
                return;
            }

            objectiveManager.DoCurrentObjective(deltaTime);
            bool run = objectiveManager.CurrentObjective.ForceRun || objectiveManager.GetCurrentPriority() > AIObjectiveManager.RunPriority;

            if (ObjectiveManager.CurrentObjective is AIObjectiveGoTo goTo && goTo.Target != null)
            {
                if (Character.CurrentHull == null)
                {
                    run = Vector2.DistanceSquared(Character.WorldPosition, goTo.Target.WorldPosition) > 300 * 300;
                }
                else
                {
                    float yDiff = goTo.Target.WorldPosition.Y - Character.WorldPosition.Y;
                    if (Math.Abs(yDiff) > 100)
                    {
                        run = true;
                    }
                    else
                    {
                        float xDiff = goTo.Target.WorldPosition.X - Character.WorldPosition.X;
                        run = Math.Abs(xDiff) > 300;
                    }
                }
            }
            if (run)
            {
                run = !AnimController.Crouching && !AnimController.IsMovingBackwards;
            }
            float currentSpeed = Character.AnimController.GetCurrentSpeed(run);

            steeringManager.Update(currentSpeed);

            bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f &&
                                   (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));

            if (steeringManager == insideSteering)
            {
                var currPath = PathSteering.CurrentPath;
                if (currPath != null && currPath.CurrentNode != null)
                {
                    if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y)
                    {
                        // Don't allow to jump from too high. The formula might require tweaking.
                        float allowedJumpHeight = Character.AnimController.ImpactTolerance / 2;
                        float height            = Math.Abs(currPath.CurrentNode.SimPosition.Y - Character.SimPosition.Y);
                        ignorePlatforms = height < allowedJumpHeight;
                    }
                }

                if (Character.IsClimbing && PathSteering.IsNextLadderSameAsCurrent)
                {
                    Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
                }
            }

            Character.AnimController.IgnorePlatforms = ignorePlatforms;

            Vector2 targetMovement = AnimController.TargetMovement;

            if (!Character.AnimController.InWater)
            {
                targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
            }

            float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed);

            targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
            targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);

            //apply speed multiplier if
            //  a. it's boosting the movement speed and the character is trying to move fast (= running)
            //  b. it's a debuff that decreases movement speed
            float speedMultiplier = Character.SpeedMultiplier;

            if (run || speedMultiplier <= 0.0f)
            {
                targetMovement *= speedMultiplier;
            }
            Character.ResetSpeedMultiplier();   // Reset, items will set the value before the next update
            Character.AnimController.TargetMovement = targetMovement;
            if (!NeedsDivingGear(Character.CurrentHull))
            {
                bool oxygenLow           = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold;
                bool highPressure        = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0;
                bool shouldKeepTheGearOn = !ObjectiveManager.IsCurrentObjective <AIObjectiveIdle>();
                bool removeDivingSuit    = oxygenLow && !highPressure;
                if (!removeDivingSuit)
                {
                    bool targetHasNoSuit = objectiveManager.CurrentOrder is AIObjectiveGoTo gtObj && gtObj.mimic && !HasDivingSuit(gtObj.Target as Character);
                    bool canDropTheSuit  = Character.CurrentHull.WaterPercentage < 1 && !Character.IsClimbing && steeringManager == insideSteering && !PathSteering.InStairs;
                    removeDivingSuit = (!shouldKeepTheGearOn || targetHasNoSuit) && canDropTheSuit;
                }
                if (removeDivingSuit)
                {
                    var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit");
                    if (divingSuit != null)
                    {
                        // TODO: take the item where it was taken from?
                        divingSuit.Drop(Character);
                    }
                }
                bool targetHasNoMask = objectiveManager.CurrentOrder is AIObjectiveGoTo gotoObjective && gotoObjective.mimic && !HasDivingMask(gotoObjective.Target as Character);
                bool takeMaskOff     = oxygenLow || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 20) || targetHasNoMask;
                if (takeMaskOff)
                {
                    var mask = Character.Inventory.FindItemByIdentifier("divingmask");
                    if (mask != null && Character.Inventory.IsInLimbSlot(mask, InvSlotType.Head))
                    {
                        // Try to put the mask in an Any slot, and drop it if that fails
                        if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>()
                        {
                            InvSlotType.Any
                        }))
                        {
                            mask.Drop(Character);
                        }
                    }
                }
            }
            if (!ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFires>() && !ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFire>())
            {
                var extinguisherItem = Character.Inventory.FindItemByIdentifier("extinguisher") ?? Character.Inventory.FindItemByTag("extinguisher");
                if (extinguisherItem != null && Character.HasEquippedItem(extinguisherItem))
                {
                    // TODO: take the item where it was taken from?
                    extinguisherItem.Drop(Character);
                }
            }
            foreach (var item in Character.Inventory.Items)
            {
                if (item == null)
                {
                    continue;
                }
                if (ObjectiveManager.CurrentObjective is AIObjectiveIdle)
                {
                    if (item.AllowedSlots.Contains(InvSlotType.RightHand | InvSlotType.LeftHand) && Character.HasEquippedItem(item))
                    {
                        // Try to put the weapon in an Any slot, and drop it if that fails
                        if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, new List <InvSlotType>()
                        {
                            InvSlotType.Any
                        }))
                        {
                            item.Drop(Character);
                        }
                    }
                }
            }

            if (Character.IsKeyDown(InputType.Aim))
            {
                var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
                if (cursorDiffX > 10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Right;
                }
                else if (cursorDiffX < -10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Left;
                }

                if (Character.SelectedConstruction != null)
                {
                    Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
                }
            }
            else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
            {
                Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
            }
        }
Exemple #4
0
        public override void Update(float deltaTime)
        {
            if (DisableCrewAI || Character.IsUnconscious)
            {
                return;
            }

            float maxDistanceToSub = 3000;

            if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null &&
                Vector2.DistanceSquared(Character.WorldPosition, SelectedAiTarget.Entity.Submarine.WorldPosition) < maxDistanceToSub * maxDistanceToSub)
            {
                if (steeringManager != insideSteering)
                {
                    insideSteering.Reset();
                }
                steeringManager = insideSteering;
            }
            else
            {
                if (steeringManager != outsideSteering)
                {
                    outsideSteering.Reset();
                }
                steeringManager = outsideSteering;
            }

            AnimController.Crouching = shouldCrouch;
            CheckCrouching(deltaTime);
            Character.ClearInputs();

            if (hullVisibilityTimer > 0)
            {
                hullVisibilityTimer--;
            }
            else
            {
                hullVisibilityTimer = hullVisibilityInterval;
                VisibleHulls        = Character.GetVisibleHulls();
            }

            objectiveManager.UpdateObjectives(deltaTime);
            if (sortTimer > 0.0f)
            {
                sortTimer -= deltaTime;
            }
            else
            {
                objectiveManager.SortObjectives();
                sortTimer = sortObjectiveInterval;
            }
            if (reactTimer > 0.0f)
            {
                reactTimer -= deltaTime;
            }
            else
            {
                if (Character.CurrentHull != null)
                {
                    VisibleHulls.ForEach(h => PropagateHullSafety(Character, h));
                }
                if (Character.SpeechImpediment < 100.0f)
                {
                    ReportProblems();
                    UpdateSpeaking();
                }
                reactTimer = reactionTime * Rand.Range(0.75f, 1.25f);
            }

            if (objectiveManager.CurrentObjective == null)
            {
                return;
            }

            objectiveManager.DoCurrentObjective(deltaTime);
            bool run = objectiveManager.CurrentObjective.ForceRun || objectiveManager.GetCurrentPriority() > AIObjectiveManager.RunPriority;

            if (ObjectiveManager.CurrentObjective is AIObjectiveGoTo goTo && goTo.Target != null)
            {
                if (Character.CurrentHull == null)
                {
                    run = Vector2.DistanceSquared(Character.WorldPosition, goTo.Target.WorldPosition) > 300 * 300;
                }
                else
                {
                    float yDiff = goTo.Target.WorldPosition.Y - Character.WorldPosition.Y;
                    if (Math.Abs(yDiff) > 100)
                    {
                        run = true;
                    }
                    else
                    {
                        float xDiff = goTo.Target.WorldPosition.X - Character.WorldPosition.X;
                        run = Math.Abs(xDiff) > 300;
                    }
                }
            }
            if (run)
            {
                run = !AnimController.Crouching && !AnimController.IsMovingBackwards;
            }
            float currentSpeed = Character.AnimController.GetCurrentSpeed(run);

            steeringManager.Update(currentSpeed);

            bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f &&
                                   (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));

            if (steeringManager == insideSteering)
            {
                var currPath = PathSteering.CurrentPath;
                if (currPath != null && currPath.CurrentNode != null)
                {
                    if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y)
                    {
                        // Don't allow to jump from too high. The formula might require tweaking.
                        float allowedJumpHeight = Character.AnimController.ImpactTolerance / 2;
                        float height            = Math.Abs(currPath.CurrentNode.SimPosition.Y - Character.SimPosition.Y);
                        ignorePlatforms = height < allowedJumpHeight;
                    }
                }

                if (Character.IsClimbing && PathSteering.IsNextLadderSameAsCurrent)
                {
                    Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
                }
            }

            Character.AnimController.IgnorePlatforms = ignorePlatforms;

            Vector2 targetMovement = AnimController.TargetMovement;

            if (!Character.AnimController.InWater)
            {
                targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
            }

            float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed);

            targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
            targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);

            //apply speed multiplier if
            //  a. it's boosting the movement speed and the character is trying to move fast (= running)
            //  b. it's a debuff that decreases movement speed
            float speedMultiplier = Character.SpeedMultiplier;

            if (run || speedMultiplier <= 0.0f)
            {
                targetMovement *= speedMultiplier;
            }
            Character.ResetSpeedMultiplier();   // Reset, items will set the value before the next update
            Character.AnimController.TargetMovement = targetMovement;

            if (!Character.LockHands)
            {
                DropUnnecessaryItems();
            }

            if (Character.IsKeyDown(InputType.Aim))
            {
                var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
                if (cursorDiffX > 10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Right;
                }
                else if (cursorDiffX < -10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Left;
                }

                if (Character.SelectedConstruction != null)
                {
                    Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
                }
            }
            else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
            {
                Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
            }
        }
Exemple #5
0
        public override void Update(float deltaTime)
        {
            if (DisableCrewAI || Character.IsUnconscious)
            {
                return;
            }

            Character.ClearInputs();

            //steeringManager = Character.AnimController.CurrentHull == null ? outdoorsSteeringManager : indoorsSteeringManager;

            if (updateObjectiveTimer > 0.0f)
            {
                updateObjectiveTimer -= deltaTime;
            }
            else
            {
                objectiveManager.UpdateObjectives();
                updateObjectiveTimer = UpdateObjectiveInterval;
            }

            objectiveManager.DoCurrentObjective(deltaTime);

            float currObjectivePriority = objectiveManager.GetCurrentPriority(Character);
            float moveSpeed             = 1.0f;

            if (currObjectivePriority > 30.0f)
            {
                moveSpeed *= Character.AnimController.InWater ? Character.AnimController.SwimSpeedMultiplier : Character.AnimController.RunSpeedMultiplier;
            }

            steeringManager.Update(moveSpeed);

            bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f &&
                                   (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));

            var currPath = (steeringManager as IndoorsSteeringManager).CurrentPath;

            if (currPath != null && currPath.CurrentNode != null)
            {
                if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y)
                {
                    ignorePlatforms = true;
                }
            }

            Character.AnimController.IgnorePlatforms = ignorePlatforms;
            (Character.AnimController as HumanoidAnimController).Crouching = false;

            if (!Character.AnimController.InWater)
            {
                Character.AnimController.TargetMovement = new Vector2(
                    Character.AnimController.TargetMovement.X,
                    MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f)) * Character.SpeedMultiplier;

                Character.SpeedMultiplier = 1.0f;
            }

            if (Character.SelectedConstruction != null && Character.SelectedConstruction.GetComponent <Items.Components.Ladder>() != null)
            {
                if (currPath != null && currPath.CurrentNode != null && currPath.CurrentNode.Ladders != null)
                {
                    Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
                }
            }

            //suit can be taken off if there character is inside a hull and there's air in the room
            bool canTakeOffSuit = Character.AnimController.CurrentHull != null &&
                                  Character.AnimController.CurrentHull.OxygenPercentage > 30.0f &&
                                  Character.AnimController.CurrentHull.Volume < Character.AnimController.CurrentHull.FullVolume * 0.3f;

            //the suit can be taken off and the character is running out of oxygen (couldn't find a tank for the suit?) or idling
            //-> take the suit off
            if (canTakeOffSuit && (Character.Oxygen < 50.0f || objectiveManager.CurrentObjective is AIObjectiveIdle))
            {
                var divingSuit = Character.Inventory.FindItem("Diving Suit");
                if (divingSuit != null)
                {
                    divingSuit.Drop(Character);
                }
            }

            if (Character.IsKeyDown(InputType.Aim))
            {
                var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
                if (cursorDiffX > 10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Right;
                }
                else if (cursorDiffX < -10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Left;
                }

                if (Character.SelectedConstruction != null)
                {
                    Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
                }
            }
            else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
            {
                Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
            }
        }
        private IEnumerable <object> DoInitialCameraTransition()
        {
            while (GameMain.Instance.LoadingScreenOpen)
            {
                yield return(CoroutineStatus.Running);
            }
            Character prevControlled = Character.Controlled;

            if (prevControlled?.AIController != null)
            {
                prevControlled.AIController.Enabled = false;
            }
            Character.Controlled = null;
            prevControlled?.ClearInputs();

            GUI.DisableHUD = true;
            while (GameMain.Instance.LoadingScreenOpen)
            {
                yield return(CoroutineStatus.Running);
            }

            if (IsFirstRound || showCampaignResetText)
            {
                overlayColor     = Color.LightGray;
                overlaySprite    = Map.CurrentLocation.Type.GetPortrait(Map.CurrentLocation.PortraitId);
                overlayTextColor = Color.Transparent;
                overlayText      = TextManager.GetWithVariables(showCampaignResetText ? "campaignend4" : "campaignstart",
                                                                new string[] { "xxxx", "yyyy" },
                                                                new string[] { Map.CurrentLocation.Name, TextManager.Get("submarineclass." + Submarine.MainSub.Info.SubmarineClass) });
                string pressAnyKeyText = TextManager.Get("pressanykey");
                float  fadeInDuration  = 2.0f;
                float  textDuration    = 10.0f;
                float  timer           = 0.0f;
                while (true)
                {
                    if (timer > fadeInDuration)
                    {
                        overlayTextBottom = pressAnyKeyText;
                        if (PlayerInput.GetKeyboardState.GetPressedKeys().Length > 0 || PlayerInput.PrimaryMouseButtonClicked())
                        {
                            break;
                        }
                    }
                    if (GameMain.GameSession == null)
                    {
                        GUI.DisableHUD = false;
                        yield return(CoroutineStatus.Success);
                    }
                    overlayTextColor = Color.Lerp(Color.Transparent, Color.White, (timer - 1.0f) / fadeInDuration);
                    timer            = Math.Min(timer + CoroutineManager.UnscaledDeltaTime, textDuration);
                    yield return(CoroutineStatus.Running);
                }
                var outpost = GameMain.GameSession.Level.StartOutpost;
                var borders = outpost.GetDockedBorders();
                borders.Location += outpost.WorldPosition.ToPoint();
                GameMain.GameScreen.Cam.Position = new Vector2(borders.X + borders.Width / 2, borders.Y - borders.Height / 2);
                float startZoom = 0.8f /
                                  ((float)Math.Max(borders.Width, borders.Height) / (float)GameMain.GameScreen.Cam.Resolution.X);
                GameMain.GameScreen.Cam.MinZoom = Math.Min(startZoom, GameMain.GameScreen.Cam.MinZoom);
                var transition = new CameraTransition(prevControlled, GameMain.GameScreen.Cam,
                                                      null, null,
                                                      fadeOut: false,
                                                      losFadeIn: true,
                                                      waitDuration: 1,
                                                      panDuration: 5,
                                                      startZoom: startZoom, endZoom: 1.0f)
                {
                    AllowInterrupt             = true,
                    RemoveControlFromCharacter = false
                };
                fadeInDuration   = 1.0f;
                timer            = 0.0f;
                overlayTextColor = Color.Transparent;
                overlayText      = "";
                while (timer < fadeInDuration)
                {
                    overlayColor = Color.Lerp(Color.LightGray, Color.Transparent, timer / fadeInDuration);
                    timer       += CoroutineManager.UnscaledDeltaTime;
                    yield return(CoroutineStatus.Running);
                }
                overlayColor = Color.Transparent;
                while (transition.Running)
                {
                    yield return(CoroutineStatus.Running);
                }
                showCampaignResetText = false;
            }
            else
            {
                ISpatialEntity transitionTarget;
                transitionTarget = (ISpatialEntity)prevControlled ?? Submarine.MainSub;

                var transition = new CameraTransition(transitionTarget, GameMain.GameScreen.Cam,
                                                      null, null,
                                                      fadeOut: false,
                                                      losFadeIn: prevControlled != null,
                                                      panDuration: 5,
                                                      startZoom: 0.5f, endZoom: 1.0f)
                {
                    AllowInterrupt             = true,
                    RemoveControlFromCharacter = false
                };
                while (transition.Running)
                {
                    yield return(CoroutineStatus.Running);
                }
            }

            if (prevControlled != null)
            {
                prevControlled.SelectedConstruction = null;
                if (prevControlled.AIController != null)
                {
                    prevControlled.AIController.Enabled = true;
                }
            }

            if (prevControlled != null)
            {
                Character.Controlled = prevControlled;
            }
            GUI.DisableHUD = false;
            yield return(CoroutineStatus.Success);
        }
        public override void Update(float deltaTime)
        {
            if (DisableCrewAI || Character.IsUnconscious)
            {
                return;
            }

            if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null)
            {
                if (steeringManager != insideSteering)
                {
                    insideSteering.Reset();
                }
                steeringManager = insideSteering;
            }
            else
            {
                if (steeringManager != outsideSteering)
                {
                    outsideSteering.Reset();
                }
                steeringManager = outsideSteering;
            }

            AnimController.Crouching = shouldCrouch;
            CheckCrouching(deltaTime);
            Character.ClearInputs();

            objectiveManager.UpdateObjectives(deltaTime);
            if (updateObjectiveTimer > 0.0f)
            {
                updateObjectiveTimer -= deltaTime;
            }
            else
            {
                objectiveManager.SortObjectives();
                updateObjectiveTimer = UpdateObjectiveInterval;
            }

            if (Character.SpeechImpediment < 100.0f)
            {
                ReportProblems();
                UpdateSpeaking();
            }

            objectiveManager.DoCurrentObjective(deltaTime);

            bool run = objectiveManager.GetCurrentPriority() > AIObjectiveManager.OrderPriority;

            if (ObjectiveManager.CurrentObjective is AIObjectiveGoTo goTo && goTo.Target != null)
            {
                if (Vector2.DistanceSquared(Character.SimPosition, goTo.Target.SimPosition) > 3 * 3)
                {
                    run = true;
                }
            }
            if (!run)
            {
                run = objectiveManager.CurrentObjective.ForceRun;
            }
            if (run)
            {
                run = !AnimController.Crouching && !AnimController.IsMovingBackwards;
            }
            float currentSpeed = Character.AnimController.GetCurrentSpeed(run);

            steeringManager.Update(currentSpeed);

            bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f &&
                                   (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X));

            if (steeringManager == insideSteering)
            {
                var currPath = PathSteering.CurrentPath;
                if (currPath != null && currPath.CurrentNode != null)
                {
                    if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y)
                    {
                        ignorePlatforms = true;
                    }
                }

                if (Character.IsClimbing && PathSteering.InLadders && PathSteering.IsNextLadderSameAsCurrent)
                {
                    Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y));
                }
            }

            Character.AnimController.IgnorePlatforms = ignorePlatforms;

            Vector2 targetMovement = AnimController.TargetMovement;

            if (!Character.AnimController.InWater)
            {
                targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f));
            }

            float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed);

            targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed);
            targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed);

            //apply speed multiplier if
            //  a. it's boosting the movement speed and the character is trying to move fast (= running)
            //  b. it's a debuff that decreases movement speed
            float speedMultiplier = Character.SpeedMultiplier;

            if (run || speedMultiplier <= 0.0f)
            {
                targetMovement *= speedMultiplier;
            }
            Character.ResetSpeedMultiplier();   // Reset, items will set the value before the next update
            Character.AnimController.TargetMovement = targetMovement;

            if (!NeedsDivingGear(Character.CurrentHull))
            {
                bool oxygenLow           = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold;
                bool highPressure        = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0;
                bool shouldKeepTheGearOn = objectiveManager.CurrentObjective.KeepDivingGearOn;

                bool removeDivingSuit = (oxygenLow && !highPressure) || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 1 && !Character.IsClimbing && steeringManager == insideSteering && !PathSteering.InStairs);
                if (removeDivingSuit)
                {
                    var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit");
                    if (divingSuit != null)
                    {
                        // TODO: take the item where it was taken from?
                        divingSuit.Drop(Character);
                    }
                }
                bool takeMaskOff = oxygenLow || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 20);
                if (takeMaskOff)
                {
                    var mask = Character.Inventory.FindItemByIdentifier("divingmask");
                    if (mask != null)
                    {
                        // Try to put the mask in an Any slot, and drop it if that fails
                        if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>()
                        {
                            InvSlotType.Any
                        }))
                        {
                            mask.Drop(Character);
                        }
                    }
                }
            }
            if (!(ObjectiveManager.CurrentOrder is AIObjectiveExtinguishFires) && !(ObjectiveManager.CurrentObjective is AIObjectiveExtinguishFire))
            {
                var extinguisherItem = Character.Inventory.FindItemByIdentifier("extinguisher") ?? Character.Inventory.FindItemByTag("extinguisher");
                if (extinguisherItem != null && Character.HasEquippedItem(extinguisherItem))
                {
                    // TODO: take the item where it was taken from?
                    extinguisherItem.Drop(Character);
                }
            }

            if (Character.IsKeyDown(InputType.Aim))
            {
                var cursorDiffX = Character.CursorPosition.X - Character.Position.X;
                if (cursorDiffX > 10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Right;
                }
                else if (cursorDiffX < -10.0f)
                {
                    Character.AnimController.TargetDir = Direction.Left;
                }

                if (Character.SelectedConstruction != null)
                {
                    Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
                }
            }
            else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
            {
                Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left;
            }

            if (Character.CurrentHull != null)
            {
                PropagateHullSafety(Character, Character.CurrentHull);
            }
        }