private void AdjustKarma(Character target, float amount, string debugKarmaChangeReason = "")
        {
            if (target == null)
            {
                return;
            }

            Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == target);

            if (client == null)
            {
                return;
            }

            //all penalties/rewards are halved when wearing a clown costume
            if (target.HasEquippedItem("clownmask") &&
                target.HasEquippedItem("clowncostume"))
            {
                amount *= 0.5f;
            }

            client.Karma += amount;
            if (TestMode)
            {
                SendKarmaNotifications(client, debugKarmaChangeReason);
            }
        }
        public void RemoveItem(Item item, bool tryEquipFromSameStack)
        {
            if (!Contains(item))
            {
                return;
            }

            bool wasEquipped = character.HasEquippedItem(item);
            var  indices     = FindIndices(item);

            base.RemoveItem(item);
#if CLIENT
            CreateSlots();
#endif
            //if the item was equipped and there are more items in the same stack, equip one of those items
            if (tryEquipFromSameStack && wasEquipped)
            {
                int limbSlot = indices.Find(j => SlotTypes[j] != InvSlotType.Any);
                foreach (int i in indices)
                {
                    var itemInSameSlot = GetItemAt(i);
                    if (itemInSameSlot != null)
                    {
                        if (TryPutItem(itemInSameSlot, limbSlot, allowSwapping: false, allowCombine: false, character))
                        {
#if CLIENT
                            visualSlots[i].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.412f);
#endif
                        }
                        break;
                    }
                }
            }
        }
Beispiel #3
0
        private static void CheckMidRoundAchievements(Character c)
        {
            if (c == null || c.Removed)
            {
                return;
            }

            if (c.HasEquippedItem("clownmask") &&
                c.HasEquippedItem("clowncostume"))
            {
                UnlockAchievement(c, "clowncostume");
            }

            if (Submarine.MainSub != null && c.Submarine == null && c.SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase))
            {
                float requiredDist = 500 / Physics.DisplayToRealWorldRatio;
                float distSquared  = Vector2.DistanceSquared(c.WorldPosition, Submarine.MainSub.WorldPosition);
                if (cachedDistances.TryGetValue(c, out var cachedDistance))
                {
                    if (cachedDistance.ShouldUpdateDistance(c.WorldPosition, Submarine.MainSub.WorldPosition))
                    {
                        cachedDistances.Remove(c);
                        cachedDistance = CalculateNewCachedDistance(c);
                        if (cachedDistance != null)
                        {
                            cachedDistances.Add(c, cachedDistance);
                        }
                    }
                }
                else
                {
                    cachedDistance = CalculateNewCachedDistance(c);
                    if (cachedDistance != null)
                    {
                        cachedDistances.Add(c, cachedDistance);
                    }
                }
                if (cachedDistance != null)
                {
                    distSquared = Math.Max(distSquared, cachedDistance.Distance * cachedDistance.Distance);
                }
                if (distSquared > requiredDist * requiredDist)
                {
                    UnlockAchievement(c, "crewaway");
                }
Beispiel #4
0
        private void AdjustKarma(Character target, float amount, string debugKarmaChangeReason = "")
        {
            if (target == null)
            {
                return;
            }

            Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == target);

            if (client == null)
            {
                return;
            }

            //all penalties/rewards are halved when wearing a clown costume
            if (target.HasEquippedItem("clownmask") &&
                target.HasEquippedItem("clowncostume"))
            {
                amount *= 0.5f;
            }

            client.Karma += amount;

            if (amount < 0.0f)
            {
                float?herpesStrength = client.Character?.CharacterHealth.GetAfflictionStrength("spaceherpes");
                var   clientMemory   = GetClientMemory(client);
                clientMemory.KarmaDecreasesInPastMinute.RemoveAll(ta => ta.Time + 60.0f < Timing.TotalTime);
                float aggregate = clientMemory.KarmaDecreasesInPastMinute.Select(ta => ta.Amount).DefaultIfEmpty().Aggregate((a, b) => a + b);
                clientMemory.KarmaDecreasesInPastMinute.Add(new ClientMemory.TimeAmount()
                {
                    Time = Timing.TotalTime, Amount = -amount
                });

                if (herpesStrength.HasValue && herpesStrength <= 0.0f && aggregate - amount > 25.0f && aggregate <= 25.0f)
                {
                    GameServer.Log($"{GameServer.ClientLogName(client)} has lost more than 25 karma in the past minute.", ServerLog.MessageType.Karma);
                }
            }

            if (TestMode)
            {
                SendKarmaNotifications(client, debugKarmaChangeReason);
            }
        }
        public static bool HasItem(Character character, string tag, string containedTag, float conditionPercentage = 0)
        {
            var item = character.Inventory.FindItemByTag(tag);

            return(item != null &&
                   item.ConditionPercentage > conditionPercentage &&
                   character.HasEquippedItem(item) &&
                   (containedTag == null ||
                    (item.ContainedItems != null &&
                     item.ContainedItems.Any(i => i.HasTag(containedTag) && i.ConditionPercentage > conditionPercentage))));
        }
Beispiel #6
0
        private static void CheckMidRoundAchievements(Character c)
        {
            if (c == null || c.Removed)
            {
                return;
            }

            if (c.HasEquippedItem("clownmask") &&
                c.HasEquippedItem("clowncostume"))
            {
                UnlockAchievement(c, "clowncostume");
            }

            if (Submarine.MainSub != null && c.Submarine == null)
            {
                float dist = 500 / Physics.DisplayToRealWorldRatio;
                if (Vector2.DistanceSquared(c.WorldPosition, Submarine.MainSub.WorldPosition) >
                    dist * dist)
                {
                    UnlockAchievement(c, "crewaway");
                }
            }
        }
Beispiel #7
0
        public static void OnCharacterKilled(Character character, CauseOfDeath causeOfDeath)
        {
#if CLIENT
            if (GameMain.Client != null || GameMain.GameSession == null)
            {
                return;
            }
#endif

            if (character != Character.Controlled &&
                causeOfDeath.Killer != null &&
                causeOfDeath.Killer == Character.Controlled)
            {
                SteamManager.IncrementStat(
                    character.SpeciesName.ToLowerInvariant() == "human" ? "humanskilled" : "monsterskilled",
                    1);
            }

            roundData.Casualties.Add(character);

            UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName);
            if (character.CurrentHull != null)
            {
                UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName + "indoors");
            }

            if (character.HasEquippedItem("clownmask") &&
                character.HasEquippedItem("clowncostume") &&
                causeOfDeath.Killer != character)
            {
                UnlockAchievement(causeOfDeath.Killer, "killclown");
            }

            if (causeOfDeath.DamageSource is Item item)
            {
                switch (item.Prefab.Identifier)
                {
                case "weldingtool":
                case "plasmacutter":
                case "wrench":
                    UnlockAchievement(causeOfDeath.Killer, "killtool");
                    break;

                case "morbusine":
                    UnlockAchievement(causeOfDeath.Killer, "killpoison");
                    break;

                case "nuclearshell":
                case "nucleardepthcharge":
                    UnlockAchievement(causeOfDeath.Killer, "killnuke");
                    break;
                }
            }

#if SERVER
            if (GameMain.Server?.TraitorManager != null)
            {
                foreach (Traitor traitor in GameMain.Server.TraitorManager.TraitorList)
                {
                    if (traitor.TargetCharacter == character)
                    {
                        //killed the target as a traitor
                        UnlockAchievement(traitor.Character, "traitorwin");
                    }
                    else if (traitor.Character == character)
                    {
                        //someone killed a traitor
                        UnlockAchievement(causeOfDeath.Killer, "killtraitor");
                    }
                }
            }
#endif
        }
        public void OnCharacterHealthChanged(Character target, Character attacker, float damage, IEnumerable <Affliction> appliedAfflictions = null)
        {
            if (target == null || attacker == null)
            {
                return;
            }
            if (target == attacker)
            {
                return;
            }

            //damaging dead characters doesn't affect karma
            if (target.IsDead || target.Removed)
            {
                return;
            }

            bool isEnemy = target.AIController is EnemyAIController || target.TeamID != attacker.TeamID;

            if (GameMain.Server.TraitorManager?.Traitors != null)
            {
                if (GameMain.Server.TraitorManager.Traitors.Any(t => t.Character == target))
                {
                    //traitors always count as enemies
                    isEnemy = true;
                }
                if (GameMain.Server.TraitorManager.Traitors.Any(t =>
                                                                t.Character == attacker &&
                                                                t.CurrentObjective != null &&
                                                                t.CurrentObjective.IsEnemy(target)))
                {
                    //target counts as an enemy to the traitor
                    isEnemy = true;
                }
            }

            bool targetIsHusk   = target.CharacterHealth?.GetAffliction <AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;
            bool attackerIsHusk = attacker.CharacterHealth?.GetAffliction <AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active;

            //huskified characters count as enemies to healthy characters and vice versa
            if (targetIsHusk != attackerIsHusk)
            {
                isEnemy = true;
            }

            if (appliedAfflictions != null)
            {
                foreach (Affliction affliction in appliedAfflictions)
                {
                    if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f))
                    {
                        continue;
                    }
                    damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength;
                }
            }

            Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == target);

            if (damage > 0 && targetClient != null)
            {
                var targetMemory = GetClientMemory(targetClient);
                targetMemory.LastAttackTime[attacker] = Timing.TotalTime;
            }

            Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attacker);

            if (attackerClient != null)
            {
                //if the attacker has been attacked by the target within the last x seconds, ignore the damage
                //(= no karma penalty from retaliating against someone who attacked you)
                var attackerMemory = GetClientMemory(attackerClient);
                if (attackerMemory.LastAttackTime.ContainsKey(target) &&
                    attackerMemory.LastAttackTime[target] > Timing.TotalTime - AllowedRetaliationTime)
                {
                    damage = Math.Min(damage, 0);
                }
            }

            //attacking/healing clowns has a smaller effect on karma
            if (target.HasEquippedItem("clownmask") &&
                target.HasEquippedItem("clowncostume"))
            {
                damage *= 0.5f;
            }

            //smaller karma penalty for attacking someone who's aiming with a weapon
            if (damage > 0.0f &&
                target.IsKeyDown(InputType.Aim) &&
                target.SelectedItems.Any(it => it != null && (it.GetComponent <MeleeWeapon>() != null || it.GetComponent <RangedWeapon>() != null)))
            {
                damage *= 0.5f;
            }

            //damage scales according to the karma of the target
            //(= smaller karma penalty from attacking someone who has a low karma)
            if (damage > 0 && targetClient != null)
            {
                damage *= MathUtils.InverseLerp(0.0f, 50.0f, targetClient.Karma);
            }

            if (isEnemy)
            {
                if (damage > 0)
                {
                    float karmaIncrease = damage * DamageEnemyKarmaIncrease;
                    if (attacker?.Info?.Job.Prefab.Identifier == "securityofficer")
                    {
                        karmaIncrease *= 2.0f;
                    }
                    AdjustKarma(attacker, karmaIncrease, "Damaged enemy");
                }
            }
            else
            {
                if (damage > 0)
                {
                    AdjustKarma(attacker, -damage * DamageFriendlyKarmaDecrease, "Damaged friendly");
                }
                else
                {
                    float karmaIncrease = -damage * HealFriendlyKarmaIncrease;
                    if (attacker?.Info?.Job.Prefab.Identifier == "medicaldoctor")
                    {
                        karmaIncrease *= 2.0f;
                    }
                    AdjustKarma(attacker, karmaIncrease, "Healed friendly");
                }
            }
        }
        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;
            }
        }
Beispiel #10
0
        public static void OnCharacterKilled(Character character, CauseOfDeath causeOfDeath)
        {
#if CLIENT
            if (GameMain.Client != null || GameMain.GameSession == null)
            {
                return;
            }
#endif

            if (character != Character.Controlled &&
                causeOfDeath.Killer != null &&
                causeOfDeath.Killer == Character.Controlled)
            {
                SteamManager.IncrementStat(
                    character.IsHuman ? "humanskilled" : "monsterskilled",
                    1);
            }

            roundData?.Casualties.Add(character);

            UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName);
            if (character.CurrentHull != null)
            {
                UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName + "indoors");
            }
            if (character.SpeciesName.EndsWith("boss"))
            {
                UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("boss", ""));
                if (character.CurrentHull != null)
                {
                    UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("boss", "") + "indoors");
                }
            }
            if (character.SpeciesName.EndsWith("_m"))
            {
                UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("_m", ""));
                if (character.CurrentHull != null)
                {
                    UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("_m", "") + "indoors");
                }
            }

            if (character.HasEquippedItem("clownmask") &&
                character.HasEquippedItem("clowncostume") &&
                causeOfDeath.Killer != character)
            {
                UnlockAchievement(causeOfDeath.Killer, "killclown");
            }

            if (character.CharacterHealth?.GetAffliction("morbusinepoisoning") != null)
            {
                UnlockAchievement(causeOfDeath.Killer, "killpoison");
            }

            if (causeOfDeath.DamageSource is Item item)
            {
                if (item.HasTag("tool"))
                {
                    UnlockAchievement(causeOfDeath.Killer, "killtool");
                }
                else
                {
                    switch (item.Prefab.Identifier)
                    {
                    case "morbusine":
                        UnlockAchievement(causeOfDeath.Killer, "killpoison");
                        break;

                    case "nuclearshell":
                    case "nucleardepthcharge":
                        UnlockAchievement(causeOfDeath.Killer, "killnuke");
                        break;
                    }
                }
            }

#if SERVER
            if (GameMain.Server?.TraitorManager != null)
            {
                if (GameMain.Server.TraitorManager.IsTraitor(character))
                {
                    UnlockAchievement(causeOfDeath.Killer, "killtraitor");
                }
            }
#endif
        }
Beispiel #11
0
 private void DropUnnecessaryItems()
 {
     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);
                 }
             }
         }
     }
 }
Beispiel #12
0
 private void UnequipUnnecessaryItems()
 {
     if (ObjectiveManager.HasActiveObjective <AIObjectiveDecontainItem>())
     {
         return;
     }
     if (findItemState == FindItemState.None || findItemState == FindItemState.Extinguisher)
     {
         if (!ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFires>() && !objectiveManager.HasActiveObjective <AIObjectiveExtinguishFire>())
         {
             var extinguisher = Character.Inventory.FindItemByTag("extinguisher");
             if (extinguisher != null && Character.HasEquippedItem(extinguisher))
             {
                 if (ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority)
                 {
                     extinguisher.Drop(Character);
                 }
                 else
                 {
                     findItemState = FindItemState.Extinguisher;
                     if (FindSuitableContainer(Character, extinguisher, out Item targetContainer))
                     {
                         findItemState = FindItemState.None;
                         itemIndex     = 0;
                         if (targetContainer != null)
                         {
                             var decontainObjective = new AIObjectiveDecontainItem(Character, extinguisher, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>());
                             decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer);
                             ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true);
                             return;
                         }
                         else
                         {
                             extinguisher.Drop(Character);
                         }
                     }
                 }
             }
         }
     }
     if (findItemState == FindItemState.None || findItemState == FindItemState.DivingSuit || findItemState == FindItemState.DivingMask)
     {
         if (!NeedsDivingGear(Character, Character.CurrentHull, out _))
         {
             bool oxygenLow           = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold;
             bool shouldKeepTheGearOn = Character.AnimController.HeadInWater ||
                                        Character.CurrentHull.WaterPercentage > 50 ||
                                        ObjectiveManager.IsCurrentObjective <AIObjectiveFindSafety>() ||
                                        ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn);
             bool            removeDivingSuit = !Character.AnimController.HeadInWater && oxygenLow;
             AIObjectiveGoTo gotoObjective    = ObjectiveManager.CurrentOrder as AIObjectiveGoTo;
             if (!removeDivingSuit)
             {
                 bool targetHasNoSuit = gotoObjective != null && gotoObjective.mimic && !HasDivingSuit(gotoObjective.Target as Character);
                 removeDivingSuit = !shouldKeepTheGearOn && (gotoObjective == null || targetHasNoSuit);
             }
             bool takeMaskOff = !Character.AnimController.HeadInWater && oxygenLow;
             if (!takeMaskOff && Character.CurrentHull.WaterPercentage < 40)
             {
                 bool targetHasNoMask = gotoObjective != null && gotoObjective.mimic && !HasDivingMask(gotoObjective.Target as Character);
                 takeMaskOff = !shouldKeepTheGearOn && (gotoObjective == null || targetHasNoMask);
             }
             if (gotoObjective != null)
             {
                 if (gotoObjective.Target is Hull h)
                 {
                     if (NeedsDivingGear(Character, h, out _))
                     {
                         removeDivingSuit = false;
                         takeMaskOff      = false;
                     }
                 }
                 else if (gotoObjective.Target is Character c)
                 {
                     if (NeedsDivingGear(Character, c.CurrentHull, out _))
                     {
                         removeDivingSuit = false;
                         takeMaskOff      = false;
                     }
                 }
                 else if (gotoObjective.Target is Item i)
                 {
                     if (NeedsDivingGear(Character, i.CurrentHull, out _))
                     {
                         removeDivingSuit = false;
                         takeMaskOff      = false;
                     }
                 }
             }
             if (findItemState == FindItemState.None || findItemState == FindItemState.DivingSuit)
             {
                 if (removeDivingSuit)
                 {
                     var divingSuit = Character.Inventory.FindItemByTag("divingsuit");
                     if (divingSuit != null)
                     {
                         if (oxygenLow || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority)
                         {
                             divingSuit.Drop(Character);
                         }
                         else
                         {
                             findItemState = FindItemState.DivingSuit;
                             if (FindSuitableContainer(Character, divingSuit, out Item targetContainer))
                             {
                                 findItemState = FindItemState.None;
                                 itemIndex     = 0;
                                 if (targetContainer != null)
                                 {
                                     var decontainObjective = new AIObjectiveDecontainItem(Character, divingSuit, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>());
                                     decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer);
                                     ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true);
                                     return;
                                 }
                                 else
                                 {
                                     divingSuit.Drop(Character);
                                 }
                             }
                         }
                     }
                 }
             }
             if (findItemState == FindItemState.None || findItemState == FindItemState.DivingMask)
             {
                 if (takeMaskOff)
                 {
                     var mask = Character.Inventory.FindItemByTag("divingmask");
                     if (mask != null && Character.Inventory.IsInLimbSlot(mask, InvSlotType.Head))
                     {
                         if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>()
                         {
                             InvSlotType.Any
                         }))
                         {
                             if (oxygenLow || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority)
                             {
                                 mask.Drop(Character);
                             }
                             else
                             {
                                 findItemState = FindItemState.DivingMask;
                                 if (FindSuitableContainer(Character, mask, out Item targetContainer))
                                 {
                                     findItemState = FindItemState.None;
                                     itemIndex     = 0;
                                     if (targetContainer != null)
                                     {
                                         var decontainObjective = new AIObjectiveDecontainItem(Character, mask, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>());
                                         decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer);
                                         ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true);
                                         return;
                                     }
                                     else
                                     {
                                         mask.Drop(Character);
                                     }
                                 }
                             }
                         }
                     }
                 }
             }
         }
     }
     if (findItemState == FindItemState.None || findItemState == FindItemState.OtherItem)
     {
         if (ObjectiveManager.IsCurrentObjective <AIObjectiveIdle>() ||
             ObjectiveManager.IsCurrentObjective <AIObjectiveOperateItem>() ||
             ObjectiveManager.IsCurrentObjective <AIObjectivePumpWater>() ||
             ObjectiveManager.IsCurrentObjective <AIObjectiveChargeBatteries>())
         {
             foreach (var item in Character.Inventory.Items)
             {
                 if (item == null)
                 {
                     continue;
                 }
                 if (Character.HasEquippedItem(item) &&
                     (Character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand) ||
                      Character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand) ||
                      Character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand | InvSlotType.LeftHand)))
                 {
                     if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, new List <InvSlotType>()
                     {
                         InvSlotType.Any
                     }))
                     {
                         if (FindSuitableContainer(Character, item, out Item targetContainer))
                         {
                             findItemState = FindItemState.None;
                             itemIndex     = 0;
                             if (targetContainer != null)
                             {
                                 var decontainObjective = new AIObjectiveDecontainItem(Character, item, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>());
                                 decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer);
                                 ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true);
                                 return;
                             }
                             else
                             {
                                 item.Drop(Character);
                             }
                         }
                         else
                         {
                             findItemState = FindItemState.OtherItem;
                         }
                     }
                 }
             }
         }
     }
 }
        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);
            }
        }