using Microsoft.Xna.Framework; using System.Linq; using System.Collections.Generic; namespace Barotrauma { class AIObjectiveFixLeaks : AIObjectiveLoop { public override Identifier Identifier { get; set; } = "fix leaks".ToIdentifier(); public override bool ForceRun => true; public override bool KeepDivingGearOn => true; protected override bool AllowInFriendlySubs => true; private Hull PrioritizedHull { get; set; } public AIObjectiveFixLeaks(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1, Hull prioritizedHull = null) : base(character, objectiveManager, priorityModifier) { PrioritizedHull = prioritizedHull; } protected override bool IsValidTarget(Gap gap) => IsValidTarget(gap, character); public static float GetLeakSeverity(Gap leak) { if (leak == null) { return 0; } float sizeFactor = MathHelper.Lerp(1, 10, MathUtils.InverseLerp(0, 200, leak.Size)); float severity = sizeFactor * leak.Open; if (!leak.IsRoomToRoom) { severity *= 10; // If there is a leak in the outer walls, the severity cannot be lower than 10, no matter how small the leak return MathHelper.Clamp(severity, 10, 100); } else { return MathHelper.Min(severity, 100); } } protected override float GetTargetPriority() { int totalLeaks = Targets.Count; if (totalLeaks == 0) { return 0; } int otherFixers = HumanAIController.CountBotsInTheCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective() && c.Character.Submarine == character.Submarine); 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 / (float)otherFixers : 1; if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountBotsInTheCrew() > 0.75f)) { // Enough fixers return 0; } return Targets.Sum(t => GetLeakSeverity(t)) * ratio; } } protected override IEnumerable GetList() => Gap.GapList; protected override AIObjective ObjectiveConstructor(Gap gap) => new AIObjectiveFixLeak(gap, character, objectiveManager, priorityModifier: PriorityModifier, isPriority: gap.FlowTargetHull == PrioritizedHull); protected override void OnObjectiveCompleted(AIObjective objective, Gap target) => HumanAIController.RemoveTargets(character, target); public static bool IsValidTarget(Gap gap, Character character) { if (gap == null) { return false; } // Don't fix a leak on a wall section set to be ignored if (gap.ConnectedWall != null) { if (gap.ConnectedWall.Sections.Any(s => s.gap == gap && s.IgnoreByAI(character))) { return false; } if (gap.ConnectedWall.MaxHealth <= 0.0f) { return false; } } if (gap.ConnectedWall == null || gap.ConnectedDoor != null || gap.Open <= 0 || gap.linkedTo.All(l => l == null)) { return false; } if (gap.Submarine == null || character.Submarine == null) { return false; } // Don't allow going into another sub, unless it's connected and of the same team and type. if (!character.Submarine.IsEntityFoundOnThisSub(gap, includingConnectedSubs: true)) { return false; } return true; } } }