コード例 #1
0
        protected override float TargetEvaluation()
        {
            if (character.SelectedConstruction != null && Targets.Any(t => character.SelectedConstruction == t && t.ConditionPercentage < 100))
            {
                // Don't stop fixing until done
                return(100);
            }
            int   otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveRepairItems>() && !c.Character.IsIncapacitated, onlyBots: true);
            int   items       = Targets.Count;
            bool  anyFixers   = otherFixers > 0;
            float ratio       = anyFixers ? items / (float)otherFixers : 1;

            if (objectiveManager.CurrentOrder == this)
            {
                return(Targets.Sum(t => 100 - t.ConditionPercentage) * ratio);
            }
            else
            {
                if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
                {
                    // Enough fixers
                    return(0);
                }
                if (RequireAdequateSkills)
                {
                    return(Targets.Sum(t => GetTargetPriority(t, character)) * ratio);
                }
                else
                {
                    return(Targets.Sum(t => 100 - t.ConditionPercentage) * ratio);
                }
            }
        }
コード例 #2
0
        protected override float TargetEvaluation()
        {
            if (Targets.None())
            {
                return(100);
            }
            if (!objectiveManager.IsOrder(this))
            {
                if (!character.IsMedic && HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.IsMedic && !c.Character.IsUnconscious))
                {
                    // Don't do anything if there's a medic on board and we are not a medic
                    return(100);
                }
            }
            float worstCondition = Targets.Min(t => GetVitalityFactor(t));

            if (Targets.Contains(character))
            {
                if (character.Bleeding > 10)
                {
                    // Enforce the highest priority when bleeding out.
                    worstCondition = 0;
                }
                // Boost the priority when wounded.
                worstCondition /= 2;
            }
            return(worstCondition);
        }
コード例 #3
0
 public override void Update(AIObjectiveManager objectiveManager, float deltaTime)
 {
     if (character.CurrentHull == null)
     {
         currenthullSafety = 0;
         Priority          = 5;
         return;
     }
     if (character.OxygenAvailable < CharacterHealth.LowOxygenThreshold)
     {
         Priority = 100;
     }
     currenthullSafety = HumanAIController.GetHullSafety(character.CurrentHull);
     if (currenthullSafety > HumanAIController.HULL_SAFETY_THRESHOLD)
     {
         Priority -= priorityDecrease * deltaTime;
     }
     else
     {
         float dangerFactor = (100 - currenthullSafety) / 100;
         Priority += dangerFactor * priorityIncrease * deltaTime;
     }
     Priority = MathHelper.Clamp(Priority, 0, 100);
     if (divingGearObjective != null && !divingGearObjective.IsCompleted() && divingGearObjective.CanBeCompleted)
     {
         Priority = Math.Max(Priority, AIObjectiveManager.OrderPriority + 10);
     }
 }
コード例 #4
0
        protected override bool ShouldInterruptSubObjective(AIObjective subObjective)
        {
            if (subObjective is AIObjectiveFindSafety)
            {
                if (character.SelectedCharacter != targetCharacter)
                {
                    return(true);
                }
                if (character.AnimController.InWater || targetCharacter.AnimController.InWater)
                {
                    return(false);
                }

                foreach (Limb limb in targetCharacter.AnimController.Limbs)
                {
                    if (!Submarine.RectContains(targetCharacter.CurrentHull.WorldRect, limb.WorldPosition))
                    {
                        return(false);
                    }
                }

                return(!character.AnimController.InWater && !targetCharacter.AnimController.InWater &&
                       HumanAIController.GetHullSafety(character.CurrentHull, character) > HumanAIController.HULL_SAFETY_THRESHOLD);
            }

            return(false);
        }
コード例 #5
0
        protected override float TargetEvaluation()
        {
            int totalLeaks = Targets.Count();

            if (totalLeaks == 0)
            {
                return(0);
            }
            int  otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveFixLeaks>() && !c.Character.IsIncapacitated, onlyBots: true);
            bool anyFixers   = otherFixers > 0;

            if (objectiveManager.IsOrder(this))
            {
                float ratio = anyFixers ? totalLeaks / (float)otherFixers : 1;
                return(Targets.Sum(t => GetLeakSeverity(t)) * ratio);
            }
            else
            {
                int   secondaryLeaks = Targets.Count(l => l.IsRoomToRoom);
                int   leaks          = totalLeaks - secondaryLeaks;
                float ratio          = leaks == 0 ? 1 : anyFixers ? leaks / otherFixers : 1;
                if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
                {
                    // Enough fixers
                    return(0);
                }
                return(Targets.Sum(t => GetLeakSeverity(t)) * ratio);
            }
        }
 public static bool IsValidTarget(Character target, Character character)
 {
     if (target == null || target.IsDead || target.Removed)
     {
         return(false);
     }
     if (target == character)
     {
         return(false);
     }
     if (HumanAIController.IsFriendly(character, target))
     {
         return(false);
     }
     if (target.Submarine == null)
     {
         return(false);
     }
     if (target.Submarine.TeamID != character.TeamID)
     {
         return(false);
     }
     if (target.CurrentHull == null)
     {
         return(false);
     }
     if (character.Submarine != null)
     {
         if (!character.Submarine.IsConnectedTo(target.Submarine))
         {
             return(false);
         }
     }
     return(true);
 }
コード例 #7
0
        public bool OrderAttendedTo(float timeSinceLastCheck = 0f)
        {
            if (!HumanAIController.IsActive(OrderedCharacter))
            {
                return(false);
            }

            // accept only the highest priority order
            if (CurrentOrder != null && OrderedCharacter.GetCurrentOrderWithTopPriority()?.Order != CurrentOrder)
            {
#if DEBUG
                ShipCommandManager.ShipCommandLog($"Order {CurrentOrder.Name} did not match current order for character {OrderedCharacter} in {this}");
#endif
                return(false);
            }

            if (!shipCommandManager.AbleToTakeOrder(OrderedCharacter))
            {
#if DEBUG
                ShipCommandManager.ShipCommandLog(OrderedCharacter + " was unable to perform assigned order in " + this);
#endif
                return(false);
            }
            return(true);
        }
コード例 #8
0
 public override float GetPriority()
 {
     if (!IsAllowed)
     {
         Priority = 0;
         return(Priority);
     }
     if (!objectiveManager.IsCurrentOrder <AIObjectiveExtinguishFires>() &&
         Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c)))
     {
         Priority = 0;
     }
     else
     {
         float yDist = Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y);
         yDist = yDist > 100 ? yDist * 3 : 0;
         float dist           = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + yDist;
         float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 5000, dist));
         if (targetHull == character.CurrentHull)
         {
             distanceFactor = 1;
         }
         float severity       = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
         float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
         float devotion       = CumulatedDevotion / 100;
         Priority = MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + (severityFactor * distanceFactor * PriorityModifier), 0, 1));
     }
     return(Priority);
 }
コード例 #9
0
 public override float GetPriority()
 {
     if (!IsAllowed)
     {
         Priority = 0;
         Abandon  = true;
     }
     else if (HumanAIController.IsTrueForAnyCrewMember(other => other != HumanAIController && other.ObjectiveManager.GetActiveObjective <AIObjectiveFixLeak>()?.Leak == Leak))
     {
         Priority = 0;
         Abandon  = true;
     }
     else
     {
         float xDist = Math.Abs(character.WorldPosition.X - Leak.WorldPosition.X);
         float yDist = Math.Abs(character.WorldPosition.Y - Leak.WorldPosition.Y);
         // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally).
         // If the target is close, ignore the distance factor alltogether so that we keep fixing the leaks that are nearby.
         float distanceFactor = isPriority || xDist < 200 && yDist < 100 ? 1 : MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 3000, xDist + yDist * 3.0f));
         float severity       = isPriority ? 1 : AIObjectiveFixLeaks.GetLeakSeverity(Leak) / 100;
         float reduction      = isPriority ? 1 : 2;
         float max            = MathHelper.Min(AIObjectiveManager.OrderPriority - reduction, 90);
         float devotion       = CumulatedDevotion / 100;
         Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * PriorityModifier), 0, 1));
     }
     return(Priority);
 }
コード例 #10
0
        private void OnArrestTargetReached()
        {
            if (HumanAIController.HasItem(character, "handlocker", out Item handCuffs) && Enemy.Stun > 0 && character.CanInteractWith(Enemy))
            {
                if (HumanAIController.TryToMoveItem(handCuffs, Enemy.Inventory))
                {
                    handCuffs.Equip(Enemy);
                }
                else
                {
#if DEBUG
                    DebugConsole.NewMessage($"{character.Name}: Failed to handcuff the target.", Color.Red);
#endif
                }
                // Confiscate stolen goods.
                foreach (var item in Enemy.Inventory.Items)
                {
                    if (item == null || item == handCuffs)
                    {
                        continue;
                    }
                    if (item.StolenDuringRound)
                    {
                        item.Drop(character);
                        character.Inventory.TryPutItem(item, character, new List <InvSlotType>()
                        {
                            InvSlotType.Any
                        });
                    }
                }
                // TODO: enable
                //character.Speak(TextManager.Get("DialogTargetArrested"), null, 3.0f, "targetarrested", 30.0f);
                IsCompleted = true;
            }
        }
コード例 #11
0
 protected override void Act(float deltaTime)
 {
     // Only continue when the get item sub objectives have been completed.
     if (subObjectives.Any())
     {
         return;
     }
     if (HumanAIController.FindSuitableContainer(character, item, ignoredContainers, ref itemIndex, out Item suitableContainer))
     {
         itemIndex = 0;
         if (suitableContainer != null)
         {
             bool equip = item.HasTag(AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR) || (
                 item.GetComponent <Wearable>() == null &&
                 item.AllowedSlots.None(s =>
                                        s == InvSlotType.Card ||
                                        s == InvSlotType.Head ||
                                        s == InvSlotType.Headset ||
                                        s == InvSlotType.InnerClothes ||
                                        s == InvSlotType.OuterClothes));
             TryAddSubObjective(ref decontainObjective, () => new AIObjectiveDecontainItem(character, item, objectiveManager, targetContainer: suitableContainer.GetComponent <ItemContainer>())
             {
                 Equip       = equip,
                 DropIfFails = true
             },
                                onCompleted: () =>
             {
                 if (equip)
                 {
                     HumanAIController.ReequipUnequipped();
                 }
                 IsCompleted = true;
             },
                                onAbandon: () =>
             {
                 if (equip)
                 {
                     HumanAIController.ReequipUnequipped();
                 }
                 if (decontainObjective != null && decontainObjective.ContainObjective != null && decontainObjective.ContainObjective.CanBeCompleted)
                 {
                     ignoredContainers.Add(suitableContainer);
                 }
                 else
                 {
                     Abandon = true;
                 }
             });
         }
         else
         {
             Abandon = true;
         }
     }
     else
     {
         objectiveManager.GetObjective <AIObjectiveIdle>().Wander(deltaTime);
     }
 }
コード例 #12
0
 public override void Reset()
 {
     base.Reset();
     moveInsideObjective  = null;
     moveInCaveObjective  = null;
     moveOutsideObjective = null;
     usingEscapeBehavior  = false;
     isSteeringThroughGap = false;
     HumanAIController.ResetEscape();
 }
コード例 #13
0
 protected override float GetPriority()
 {
     if (!IsAllowed || Item.IgnoreByAI(character))
     {
         Priority = 0;
         Abandon  = true;
         if (IsRepairing())
         {
             Item.Repairables.ForEach(r => r.StopRepairing(character));
         }
         return(Priority);
     }
     if (HumanAIController.IsItemRepairedByAnother(Item, out _))
     {
         Priority    = 0;
         IsCompleted = true;
     }
     else
     {
         float distanceFactor = 1;
         if (!isPriority && Item.CurrentHull != character.CurrentHull)
         {
             float yDist = Math.Abs(character.WorldPosition.Y - Item.WorldPosition.Y);
             yDist = yDist > 100 ? yDist * 5 : 0;
             float dist = Math.Abs(character.WorldPosition.X - Item.WorldPosition.X) + yDist;
             distanceFactor = MathHelper.Lerp(1, 0.25f, MathUtils.InverseLerp(0, 4000, dist));
         }
         float requiredSuccessFactor = objectiveManager.HasOrder <AIObjectiveRepairItems>() ? 0 : AIObjectiveRepairItems.RequiredSuccessFactor;
         float severity      = isPriority ? 1 : AIObjectiveRepairItems.GetTargetPriority(Item, character, requiredSuccessFactor) / 100;
         bool  isSelected    = IsRepairing();
         float selectedBonus = isSelected ? 100 - MaxDevotion : 0;
         float devotion      = (CumulatedDevotion + selectedBonus) / 100;
         float reduction     = isPriority ? 1 : isSelected ? 2 : 3;
         float max           = AIObjectiveManager.LowestOrderPriority - reduction;
         float highestWeight = -1;
         foreach (string tag in Item.Prefab.Tags)
         {
             if (JobPrefab.ItemRepairPriorities.TryGetValue(tag, out float weight) && weight > highestWeight)
             {
                 highestWeight = weight;
             }
         }
         if (highestWeight == -1)
         {
             // Predefined weight not found.
             highestWeight = 1;
         }
         Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (severity * distanceFactor * highestWeight * PriorityModifier), 0, 1));
     }
     return(Priority);
 }
コード例 #14
0
        private void Move(float deltaTime)
        {
            switch (Mode)
            {
            case CombatMode.Offensive:
            case CombatMode.Arrest:
                Engage(deltaTime);
                break;

            case CombatMode.Defensive:
                if (character.IsOnPlayerTeam && !Enemy.IsPlayer && objectiveManager.IsCurrentOrder <AIObjectiveGoTo>())
                {
                    if ((character.CurrentHull == null || character.CurrentHull == Enemy.CurrentHull) && sqrDistance < 200 * 200)
                    {
                        Engage(deltaTime);
                    }
                    else
                    {
                        // Keep following the goto target
                        var gotoObjective = objectiveManager.GetOrder <AIObjectiveGoTo>();
                        if (gotoObjective != null)
                        {
                            gotoObjective.ForceAct(deltaTime);
                            if (!character.AnimController.InWater)
                            {
                                HumanAIController.FaceTarget(Enemy);
                                ForceWalk = true;
                                HumanAIController.AutoFaceMovement = false;
                            }
                        }
                        else
                        {
                            SteeringManager.Reset();
                        }
                    }
                }
                else
                {
                    Retreat(deltaTime);
                }
                break;

            case CombatMode.Retreat:
                Retreat(deltaTime);
                break;

            default:
                throw new NotImplementedException();
            }
        }
コード例 #15
0
 private bool IsOperatedByAnother(ItemComponent target)
 {
     foreach (var c in Character.CharacterList)
     {
         if (c == character)
         {
             continue;
         }
         if (!HumanAIController.IsFriendly(c))
         {
             continue;
         }
         if (c.SelectedConstruction != target.Item)
         {
             continue;
         }
         // If the other character is player, don't try to operate
         if (c.IsRemotePlayer || Character.Controlled == c)
         {
             return(true);
         }
         if (c.AIController is HumanAIController humanAi)
         {
             // If the other character is ordered to operate the item, let him do it
             if (humanAi.ObjectiveManager.IsCurrentOrder <AIObjectiveOperateItem>())
             {
                 return(true);
             }
             else
             {
                 if (target is Steering)
                 {
                     // Steering is hard-coded -> cannot use the required skills collection defined in the xml
                     return(character.GetSkillLevel("helm") <= c.GetSkillLevel("helm"));
                 }
                 else
                 {
                     return(target.DegreeOfSuccess(character) <= target.DegreeOfSuccess(c));
                 }
             }
         }
         else
         {
             // Shouldn't go here, unless we allow non-humans to operate items
             return(false);
         }
     }
     return(false);
 }
コード例 #16
0
        protected override void Act(float deltaTime)
        {
            if (abortCondition != null && abortCondition())
            {
                Abandon = true;
                return;
            }
            if (AllowCoolDown)
            {
                coolDownTimer -= deltaTime;
            }
            if (seekAmmunitionObjective == null && seekWeaponObjective == null)
            {
                if (Mode != CombatMode.Retreat && TryArm() && !IsEnemyDisabled)
                {
                    OperateWeapon(deltaTime);
                }
                if (HoldPosition)
                {
                    SteeringManager.Reset();
                }
                else if (seekAmmunitionObjective == null && seekWeaponObjective == null)
                {
                    Move(deltaTime);
                }
                switch (Mode)
                {
                case CombatMode.Offensive:
                    if (TargetEliminated && objectiveManager.IsCurrentOrder <AIObjectiveFightIntruders>())
                    {
                        character.Speak(TextManager.Get("DialogTargetDown"), null, 3.0f, "targetdown", 30.0f);
                    }
                    break;

                case CombatMode.Arrest:
                    if (HumanAIController.HasItem(Enemy, "handlocker", out _, requireEquipped: true))
                    {
                        IsCompleted = true;
                    }
                    else if (Enemy.IsKnockedDown &&
                             !objectiveManager.IsCurrentObjective <AIObjectiveFightIntruders>() &&
                             !HumanAIController.HasItem(character, "handlocker", out _, requireEquipped: false))
                    {
                        IsCompleted = true;
                    }
                    break;
                }
            }
        }
コード例 #17
0
        public override float GetPriority()
        {
            if (Character.CharacterList.Any(c => c.CurrentHull == targetHull && !HumanAIController.IsFriendly(c)))
            {
                return(0);
            }
            // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally)
            float dist           = Math.Abs(character.WorldPosition.X - targetHull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - targetHull.WorldPosition.Y) * 2.0f;
            float distanceFactor = MathHelper.Lerp(1, 0.1f, MathUtils.InverseLerp(0, 10000, dist));
            float severity       = AIObjectiveExtinguishFires.GetFireSeverity(targetHull);
            float severityFactor = MathHelper.Lerp(0, 1, severity / 100);
            float devotion       = Math.Min(Priority, 10) / 100;

            return(MathHelper.Lerp(0, 100, MathHelper.Clamp(devotion + severityFactor * distanceFactor, 0, 1)));
        }
コード例 #18
0
 public override float GetPriority()
 {
     if (character.TeamID == CharacterTeamType.FriendlyNPC && Enemy != null)
     {
         if (Enemy.Submarine == null || (Enemy.Submarine.TeamID != character.TeamID && Enemy.Submarine != character.Submarine))
         {
             Priority = 0;
             Abandon = true;
             return Priority;
         }
     }
     float damageFactor = MathUtils.InverseLerp(0.0f, 5.0f, HumanAIController.GetDamageDoneByAttacker(Enemy) / 100.0f);
     Priority = TargetEliminated ? 0 : Math.Min((95 + damageFactor) * PriorityModifier, 100);
     return Priority;
 }
コード例 #19
0
 public static bool IsValidTarget(Character target, Character character)
 {
     if (target == null || target.IsDead || target.Removed)
     {
         return(false);
     }
     if (!HumanAIController.IsFriendly(character, target))
     {
         return(false);
     }
     if (character.AIController is HumanAIController humanAI)
     {
         if (GetVitalityFactor(target) > GetVitalityThreshold(humanAI.ObjectiveManager))
         {
             return(false);
         }
     }
     else
     {
         if (GetVitalityFactor(target) > vitalityThreshold)
         {
             return(false);
         }
     }
     if (target.Submarine == null || character.Submarine == null)
     {
         return(false);
     }
     if (target.Submarine.TeamID != character.Submarine.TeamID)
     {
         return(false);
     }
     if (target.CurrentHull == null)
     {
         return(false);
     }
     if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true))
     {
         return(false);
     }
     // Don't go into rooms that have enemies
     if (Character.CharacterList.Any(c => c.CurrentHull == target.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c)))
     {
         return(false);
     }
     return(true);
 }
コード例 #20
0
        public bool CreateCombatBehavior(MentalType mentalType)
        {
            Character mentalAttackTarget = Character.CharacterList.Where(
                possibleTarget => HumanAIController.IsActive(possibleTarget) &&
                (possibleTarget.TeamID != character.TeamID || mentalType == MentalType.Berserk) &&
                humanAIController.VisibleHulls.Contains(possibleTarget.CurrentHull) &&
                possibleTarget != character).GetRandom();

            if (mentalAttackTarget == null)
            {
                return(false);
            }

            var  combatMode = AIObjectiveCombat.CombatMode.None;
            bool holdFire   = mentalType == MentalType.Afraid && character.IsSecurity;

            switch (mentalType)
            {
            case MentalType.Afraid:
                combatMode = character.IsSecurity ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Retreat;
                break;

            case MentalType.Desperate:
                // might be unnecessary to explicitly declare as arrest against non-humans
                combatMode = character.IsSecurity && mentalAttackTarget.IsHuman ? AIObjectiveCombat.CombatMode.Arrest : AIObjectiveCombat.CombatMode.Defensive;
                break;

            case MentalType.Berserk:
                combatMode = AIObjectiveCombat.CombatMode.Offensive;
                break;
            }

            // using this as an explicit time-out for the behavior. it's possible it will never run out because of the manager being disabled, but combat objective has failsafes for that
            mentalBehaviorTimer = MentalBehaviorInterval;
            humanAIController.AddCombatObjective(combatMode, mentalAttackTarget, allowHoldFire: holdFire, abortCondition: obj => mentalBehaviorTimer <= 0f);
            string textIdentifier = $"dialogmentalstatereaction{combatMode.ToString().ToLowerInvariant()}";

            character.Speak(TextManager.Get(textIdentifier), delay: Rand.Range(0.5f, 1.0f), identifier: textIdentifier, minDurationBetweenSimilar: 25f);

            if (mentalType == MentalType.Berserk && !character.HasTeamChange(MentalTeamChange))
            {
                // TODO: could this be handled in the switch block above?
                character.TryAddNewTeamChange(MentalTeamChange, new ActiveTeamChange(CharacterTeamType.None, ActiveTeamChange.TeamChangePriorities.Absolute, aggressiveBehavior: true));
            }

            return(true);
        }
コード例 #21
0
 public static bool IsValidTarget(Character target, Character character)
 {
     if (target == null || target.Removed)
     {
         return(false);
     }
     if (target.IsDead)
     {
         return(false);
     }
     if (target.IsUnconscious && target.Params.Health.ConstantHealthRegeneration <= 0.0f)
     {
         return(false);
     }
     if (target == character)
     {
         return(false);
     }
     if (target.Submarine == null)
     {
         return(false);
     }
     if (character.Submarine == null)
     {
         return(false);
     }
     if (target.CurrentHull == null)
     {
         return(false);
     }
     if (HumanAIController.IsFriendly(character, target))
     {
         return(false);
     }
     if (!character.Submarine.IsConnectedTo(target.Submarine))
     {
         return(false);
     }
     if (target.HasAbilityFlag(AbilityFlags.IgnoredByEnemyAI))
     {
         return(false);
     }
     return(true);
 }
コード例 #22
0
 public static bool IsValidTarget(Character target, Character character)
 {
     if (target == null || target.IsDead || target.Removed)
     {
         return(false);
     }
     if (!HumanAIController.IsFriendly(character, target))
     {
         return(false);
     }
     if (character.AIController is HumanAIController humanAI)
     {
         if (target.Bleeding < 1 && target.Vitality / target.MaxVitality > GetVitalityThreshold(humanAI.ObjectiveManager))
         {
             return(false);
         }
     }
     else
     {
         if (target.Bleeding < 1 && target.Vitality / target.MaxVitality > vitalityThreshold)
         {
             return(false);
         }
     }
     if (target.Submarine == null)
     {
         return(false);
     }
     if (target.Submarine.TeamID != character.Submarine.TeamID)
     {
         return(false);
     }
     if (target.CurrentHull == null)
     {
         return(false);
     }
     if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target.CurrentHull, true))
     {
         return(false);
     }
     return(true);
 }
コード例 #23
0
        protected override float TargetEvaluation()
        {
            var selectedItem = character.SelectedConstruction;

            if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100)
            {
                // Don't stop fixing until completely done
                return(100);
            }
            int otherFixers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveRepairItems>() && !c.Character.IsIncapacitated, onlyBots: true);
            int items       = Targets.Count;

            if (items == 0)
            {
                return(0);
            }
            bool  anyFixers = otherFixers > 0;
            float ratio     = anyFixers ? items / (float)otherFixers : 1;

            if (objectiveManager.IsOrder(this))
            {
                return(Targets.Sum(t => 100 - t.ConditionPercentage));
            }
            else
            {
                if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountCrew(onlyBots: true) > 0.75f))
                {
                    // Enough fixers
                    return(0);
                }
                if (RequireAdequateSkills)
                {
                    return(Targets.Sum(t => GetTargetPriority(t, character, RequiredSuccessFactor)) * ratio);
                }
                else
                {
                    return(Targets.Sum(t => 100 - t.ConditionPercentage) * ratio);
                }
            }
        }
コード例 #24
0
        protected override float TargetEvaluation()
        {
            if (!character.IsOnPlayerTeam)
            {
                return(Targets.None() ? 0 : 100);
            }
            int totalEnemies = Targets.Count();

            if (totalEnemies == 0)
            {
                return(0);
            }
            if (character.IsSecurity)
            {
                return(100);
            }
            if (objectiveManager.IsOrder(this))
            {
                return(100);
            }
            return(HumanAIController.IsTrueForAnyCrewMember(c => c.Character.IsSecurity && !c.Character.IsIncapacitated && c.Character.Submarine == character.Submarine) ? 0 : 100);
        }
コード例 #25
0
        protected override float TargetEvaluation()
        {
            int   otherRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective <AIObjectiveRescueAll>(), onlyBots: true);
            int   targetCount   = Targets.Count;
            bool  anyRescuers   = otherRescuers > 0;
            float ratio         = anyRescuers ? targetCount / (float)otherRescuers : 1;

            if (objectiveManager.CurrentOrder == this)
            {
                return(Targets.Min(t => GetVitalityFactor(t)) / ratio);
            }
            else
            {
                float multiplier = 1;
                if (anyRescuers)
                {
                    float mySkill        = character.GetSkillLevel("medical");
                    int   betterRescuers = HumanAIController.CountCrew(c => c != HumanAIController && c.Character.Info.Job.GetSkillLevel("medical") >= mySkill, onlyBots: true);
                    if (targetCount / (float)betterRescuers <= 1)
                    {
                        // Enough rescuers
                        return(100);
                    }
                    else
                    {
                        bool foundOtherMedics = HumanAIController.IsTrueForAnyCrewMember(c => c != HumanAIController && c.Character.Info.Job.Prefab.Identifier == "medicaldoctor");
                        if (foundOtherMedics)
                        {
                            if (character.Info.Job.Prefab.Identifier != "medicaldoctor")
                            {
                                // Double the vitality factor -> less likely to take action
                                multiplier = 2;
                            }
                        }
                    }
                }
                return(Targets.Min(t => GetVitalityFactor(t)) / ratio * multiplier);
            }
        }
コード例 #26
0
        protected override AIObjective ObjectiveConstructor(Character target)
        {
            var combatObjective = new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier);

            if (character.TeamID == Character.TeamType.FriendlyNPC && target.TeamID == Character.TeamType.Team1 && GameMain.GameSession?.GameMode is CampaignMode campaign)
            {
                var reputation = campaign.Map?.CurrentLocation?.Reputation;
                if (reputation != null && reputation.NormalizedValue < Reputation.HostileThreshold)
                {
                    combatObjective.holdFireCondition = () =>
                    {
                        //hold fire while the enemy is in the airlock (except if they've attacked us)
                        if (HumanAIController.GetDamageDoneByAttacker(target) > 0.0f)
                        {
                            return(false);
                        }
                        return(target.CurrentHull == null || target.CurrentHull.OutpostModuleTags.Any(t => t.Equals("airlock", System.StringComparison.OrdinalIgnoreCase)));
                    };
                    character.Speak(TextManager.Get("dialogenteroutpostwarning"), null, Rand.Range(0.5f, 1.0f), "leaveoutpostwarning", 30.0f);
                }
            }
            return(combatObjective);
        }
コード例 #27
0
        protected override void Act(float deltaTime)
        {
            // Only continue when the get item sub objectives have been completed.
            if (subObjectives.Any())
            {
                return;
            }
            foreach (Repairable repairable in Item.Repairables)
            {
                if (!repairable.HasRequiredItems(character, false))
                {
                    //make sure we have all the items required to fix the target item
                    foreach (var kvp in repairable.requiredItems)
                    {
                        foreach (RelatedItem requiredItem in kvp.Value)
                        {
                            subObjectives.Add(new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true));
                        }
                    }
                    return;
                }
            }
            if (repairTool == null)
            {
                FindRepairTool();
            }
            if (repairTool != null)
            {
                var containedItems = repairTool.Item.ContainedItems;
                if (containedItems == null)
                {
#if DEBUG
                    DebugConsole.ThrowError($"{character.Name}: AIObjectiveRepairItem failed - the item \"" + repairTool + "\" has no proper inventory");
#endif
                    Abandon = true;
                    return;
                }
                // Drop empty tanks
                foreach (Item containedItem in containedItems)
                {
                    if (containedItem == null)
                    {
                        continue;
                    }
                    if (containedItem.Condition <= 0.0f)
                    {
                        containedItem.Drop(character);
                    }
                }
                RelatedItem item = null;
                Item        fuel = null;
                foreach (RelatedItem requiredItem in repairTool.requiredItems[RelatedItem.RelationType.Contained])
                {
                    item = requiredItem;
                    fuel = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
                    if (fuel != null)
                    {
                        break;
                    }
                }
                if (fuel == null)
                {
                    RemoveSubObjective(ref goToObjective);
                    TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, item.Identifiers, repairTool.Item.GetComponent <ItemContainer>(), objectiveManager),
                                       onCompleted: () => RemoveSubObjective(ref refuelObjective),
                                       onAbandon: () => Abandon = true);
                    return;
                }
            }
            if (character.CanInteractWith(Item, out _, checkLinked: false))
            {
                HumanAIController.FaceTarget(Item);
                if (repairTool != null)
                {
                    OperateRepairTool(deltaTime);
                }
                foreach (Repairable repairable in Item.Repairables)
                {
                    if (repairable.CurrentFixer != null && repairable.CurrentFixer != character)
                    {
                        // Someone else is repairing the target. Abandon the objective if the other is better at this than us.
                        Abandon = repairable.DegreeOfSuccess(character) < repairable.DegreeOfSuccess(repairable.CurrentFixer);
                    }
                    if (!Abandon)
                    {
                        if (character.SelectedConstruction != Item)
                        {
                            if (!Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) &&
                                !Item.TryInteract(character, ignoreRequiredItems: true, forceActionKey: true))
                            {
                                Abandon = true;
                            }
                        }
                        if (previousCondition == -1)
                        {
                            previousCondition = Item.Condition;
                        }
                        else if (Item.Condition < previousCondition)
                        {
                            // If the current condition is less than the previous condition, we can't complete the task, so let's abandon it. The item is probably deteriorating at a greater speed than we can repair it.
                            Abandon = true;
                        }
                    }
                    if (Abandon)
                    {
                        if (IsRepairing)
                        {
                            character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
                        }
                        repairable.StopRepairing(character);
                    }
                    else if (repairable.CurrentFixer != character)
                    {
                        repairable.StartRepairing(character, Repairable.FixActions.Repair);
                    }
                    break;
                }
            }
            else
            {
                RemoveSubObjective(ref refuelObjective);
                // If cannot reach the item, approach it.
                TryAddSubObjective(ref goToObjective,
                                   constructor: () =>
                {
                    previousCondition = -1;
                    var objective     = new AIObjectiveGoTo(Item, character, objectiveManager)
                    {
                        // Don't stop in ladders, because we can't interact with other items while holding the ladders.
                        endNodeFilter = node => node.Waypoint.Ladders == null
                    };
                    if (repairTool != null)
                    {
                        objective.CloseEnough = repairTool.Range * 0.75f;
                    }
                    return(objective);
                },
                                   onAbandon: () =>
                {
                    Abandon = true;
                    if (IsRepairing)
                    {
                        character.Speak(TextManager.GetWithVariable("DialogCannotRepair", "[itemname]", Item.Name, true), null, 0.0f, "cannotrepair", 10.0f);
                    }
                });
            }
        }
コード例 #28
0
        protected virtual void PutItem(Item item, int i, Character user, bool removeItem = true, bool createNetworkEvent = true)
        {
            if (i < 0 || i >= slots.Length)
            {
                string errorMsg = "Inventory.PutItem failed: index was out of range(" + i + ").\n" + Environment.StackTrace.CleanupStackTrace();
                GameAnalyticsManager.AddErrorEventOnce("Inventory.PutItem:IndexOutOfRange", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
                return;
            }

            if (Owner == null)
            {
                return;
            }

            Inventory prevInventory      = item.ParentInventory;
            Inventory prevOwnerInventory = item.FindParentInventory(inv => inv is CharacterInventory);

            if (createNetworkEvent)
            {
                CreateNetworkEvent();
                //also delay syncing the inventory the item was inside
                if (prevInventory != null && prevInventory != this)
                {
                    prevInventory.syncItemsDelay = 1.0f;
                }
            }

            if (removeItem)
            {
                item.Drop(user);
                if (item.ParentInventory != null)
                {
                    item.ParentInventory.RemoveItem(item);
                }
            }

            slots[i].Add(item);
            item.ParentInventory = this;

#if CLIENT
            if (visualSlots != null)
            {
                visualSlots[i]?.ShowBorderHighlight(Color.White, 0.1f, 0.4f);
            }
#endif

            if (item.body != null)
            {
                item.body.Enabled  = false;
                item.body.BodyType = FarseerPhysics.BodyType.Dynamic;
            }

#if SERVER
            if (prevOwnerInventory is CharacterInventory characterInventory && characterInventory != this && Owner == user)
            {
                var client = GameMain.Server?.ConnectedClients?.Find(cl => cl.Character == user);
                GameMain.Server?.KarmaManager.OnItemTakenFromPlayer(characterInventory, client, item);
            }
#endif
            if (this is CharacterInventory)
            {
                if (prevInventory != this && prevOwnerInventory != this)
                {
                    HumanAIController.ItemTaken(item, user);
                }
            }
            else
            {
                if (item.FindParentInventory(inv => inv is CharacterInventory) is CharacterInventory currentInventory)
                {
                    if (currentInventory != prevInventory)
                    {
                        HumanAIController.ItemTaken(item, user);
                    }
                }
            }
        }
コード例 #29
0
        protected override void Act(float deltaTime)
        {
            if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
            {
                Abandon = true;
                return;
            }
            var otherRescuer = targetCharacter.SelectedBy;

            if (otherRescuer != null && otherRescuer != character)
            {
                // Someone else is rescuing/holding the target.
                Abandon = otherRescuer.IsPlayer || character.GetSkillLevel("medical") < otherRescuer.GetSkillLevel("medical");
                return;
            }
            if (targetCharacter != character)
            {
                if (targetCharacter.IsIncapacitated)
                {
                    // Check if the character needs more oxygen
                    if (!ignoreOxygen && character.SelectedCharacter == targetCharacter || character.CanInteractWith(targetCharacter))
                    {
                        // Replace empty oxygen and welding fuel.
                        if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.HEAVY_DIVING_GEAR, out IEnumerable <Item> suits, requireEquipped: true))
                        {
                            Item suit = suits.FirstOrDefault();
                            if (suit != null)
                            {
                                AIController.UnequipEmptyItems(character, suit);
                                AIController.UnequipContainedItems(character, suit, it => it.HasTag("weldingfuel"));
                            }
                        }
                        else if (HumanAIController.HasItem(targetCharacter, AIObjectiveFindDivingGear.LIGHT_DIVING_GEAR, out IEnumerable <Item> masks, requireEquipped: true))
                        {
                            Item mask = masks.FirstOrDefault();
                            if (mask != null)
                            {
                                AIController.UnequipEmptyItems(character, mask);
                                AIController.UnequipContainedItems(character, mask, it => it.HasTag("weldingfuel"));
                            }
                        }
                        bool ShouldRemoveDivingSuit() => targetCharacter.OxygenAvailable < CharacterHealth.InsufficientOxygenThreshold && targetCharacter.CurrentHull?.LethalPressure <= 0;

                        if (ShouldRemoveDivingSuit())
                        {
                            suits.ForEach(suit => suit.Drop(character));
                        }
                        else if (suits.Any() && suits.None(s => s.OwnInventory?.AllItems != null && s.OwnInventory.AllItems.Any(it => it.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) && it.ConditionPercentage > 0)))
                        {
                            // The target has a suit equipped with an empty oxygen tank.
                            // Can't remove the suit, because the target needs it.
                            // If we happen to have an extra oxygen tank in the inventory, let's swap it.
                            Item spareOxygenTank = FindOxygenTank(targetCharacter) ?? FindOxygenTank(character);
                            if (spareOxygenTank != null)
                            {
                                Item suit = suits.FirstOrDefault();
                                if (suit != null)
                                {
                                    // Insert the new oxygen tank
                                    TryAddSubObjective(ref replaceOxygenObjective, () => new AIObjectiveContainItem(character, spareOxygenTank, suit.GetComponent <ItemContainer>(), objectiveManager),
                                                       onCompleted: () => RemoveSubObjective(ref replaceOxygenObjective),
                                                       onAbandon: () =>
                                    {
                                        RemoveSubObjective(ref replaceOxygenObjective);
                                        ignoreOxygen = true;
                                        if (ShouldRemoveDivingSuit())
                                        {
                                            suits.ForEach(suit => suit.Drop(character));
                                        }
                                    });
                                    return;
                                }
                            }

                            Item FindOxygenTank(Character c) =>
                            c.Inventory.FindItem(i =>
                                                 i.HasTag(AIObjectiveFindDivingGear.OXYGEN_SOURCE) &&
                                                 i.ConditionPercentage > 1 &&
                                                 i.FindParentInventory(inv => inv.Owner is Item otherItem && otherItem.HasTag("diving")) == null,
                                                 recursive: true);
                        }
                    }
                    if (HumanAIController.GetHullSafety(targetCharacter.CurrentHull, targetCharacter) < HumanAIController.HULL_SAFETY_THRESHOLD)
                    {
                        // Incapacitated target is not in a safe place -> Move to a safe place first
                        if (character.SelectedCharacter != targetCharacter)
                        {
                            if (targetCharacter.CurrentHull != null && HumanAIController.VisibleHulls.Contains(targetCharacter.CurrentHull) && targetCharacter.CurrentHull.DisplayName != null)
                            {
                                character.Speak(TextManager.GetWithVariables("DialogFoundUnconsciousTarget", new string[2] {
                                    "[targetname]", "[roomname]"
                                },
                                                                             new string[2] {
                                    targetCharacter.Name, targetCharacter.CurrentHull.DisplayName
                                }, new bool[2] {
                                    false, true
                                }),
                                                null, 1.0f, "foundunconscioustarget" + targetCharacter.Name, 60.0f);
                            }
                            // Go to the target and select it
                            if (!character.CanInteractWith(targetCharacter))
                            {
                                RemoveSubObjective(ref replaceOxygenObjective);
                                RemoveSubObjective(ref goToObjective);
                                TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
                                {
                                    CloseEnough        = CloseEnoughToTreat,
                                    DialogueIdentifier = "dialogcannotreachpatient",
                                    TargetName         = targetCharacter.DisplayName
                                },
                                                   onCompleted: () => RemoveSubObjective(ref goToObjective),
                                                   onAbandon: () =>
                                {
                                    RemoveSubObjective(ref goToObjective);
                                    Abandon = true;
                                });
                            }
                            else
                            {
                                character.SelectCharacter(targetCharacter);
                            }
                        }
                        else
                        {
                            // Drag the character into safety
                            if (safeHull == null)
                            {
                                if (findHullTimer > 0)
                                {
                                    findHullTimer -= deltaTime;
                                }
                                else
                                {
                                    safeHull      = objectiveManager.GetObjective <AIObjectiveFindSafety>().FindBestHull(HumanAIController.VisibleHulls);
                                    findHullTimer = findHullInterval * Rand.Range(0.9f, 1.1f);
                                }
                            }
                            if (safeHull != null && character.CurrentHull != safeHull)
                            {
                                RemoveSubObjective(ref replaceOxygenObjective);
                                RemoveSubObjective(ref goToObjective);
                                TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(safeHull, character, objectiveManager),
                                                   onCompleted: () => RemoveSubObjective(ref goToObjective),
                                                   onAbandon: () =>
                                {
                                    RemoveSubObjective(ref goToObjective);
                                    safeHull = character.CurrentHull;
                                });
                            }
                        }
                    }
                }
            }

            if (subObjectives.Any())
            {
                return;
            }

            if (targetCharacter != character && !character.CanInteractWith(targetCharacter))
            {
                RemoveSubObjective(ref replaceOxygenObjective);
                RemoveSubObjective(ref goToObjective);
                // Go to the target and select it
                TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(targetCharacter, character, objectiveManager)
                {
                    CloseEnough        = CloseEnoughToTreat,
                    DialogueIdentifier = "dialogcannotreachpatient",
                    TargetName         = targetCharacter.DisplayName
                },
                                   onCompleted: () => RemoveSubObjective(ref goToObjective),
                                   onAbandon: () =>
                {
                    RemoveSubObjective(ref goToObjective);
                    Abandon = true;
                });
            }
            else
            {
                // We can start applying treatment
                if (character != targetCharacter && character.SelectedCharacter != targetCharacter)
                {
                    if (targetCharacter.CurrentHull.DisplayName != null)
                    {
                        character.Speak(TextManager.GetWithVariables("DialogFoundWoundedTarget", new string[2] {
                            "[targetname]", "[roomname]"
                        },
                                                                     new string[2] {
                            targetCharacter.Name, targetCharacter.CurrentHull.DisplayName
                        }, new bool[2] {
                            false, true
                        }),
                                        null, 1.0f, "foundwoundedtarget" + targetCharacter.Name, 60.0f);
                    }
                }
                GiveTreatment(deltaTime);
            }
        }
コード例 #30
0
        protected override bool CheckObjectiveSpecific()
        {
            if (character.LockHands || targetCharacter == null || targetCharacter.CurrentHull == null || targetCharacter.Removed || targetCharacter.IsDead)
            {
                Abandon = true;
                return(false);
            }
            // Don't go into rooms that have enemies
            if (Character.CharacterList.Any(c => c.CurrentHull == targetCharacter.CurrentHull && !HumanAIController.IsFriendly(character, c) && HumanAIController.IsActive(c)))
            {
                Abandon = true;
                return(false);
            }
            bool isCompleted =
                AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) ||
                targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold);

            if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
            {
                character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name),
                                null, 1.0f, "targethealed" + targetCharacter.Name, 60.0f);
            }
            return(isCompleted);
        }