using System; using System.Collections.Generic; using System.Linq; using Barotrauma.Items.Components; using Barotrauma.Extensions; namespace Barotrauma { class AIObjectiveRepairItems : AIObjectiveLoop { public override string DebugTag => "repair items"; /// /// Should the character only attempt to fix items they have the skills to fix, or any damaged item /// public bool RequireAdequateSkills; public override bool AllowMultipleInstances => true; public override bool IsDuplicate(T otherObjective) => (otherObjective as AIObjective) is AIObjectiveRepairItems repairObjective && repairObjective.RequireAdequateSkills == RequireAdequateSkills; public AIObjectiveRepairItems(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { } protected override void CreateObjectives() { foreach (var item in Targets) { foreach (Repairable repairable in item.Repairables) { if (!Objectives.TryGetValue(item, out AIObjective objective)) { objective = ObjectiveConstructor(item); Objectives.Add(item, objective); if (!subObjectives.Contains(objective)) { subObjectives.Add(objective); } objective.Completed += () => { Objectives.Remove(item); OnObjectiveCompleted(objective, item); }; objective.Abandoned += () => { Objectives.Remove(item); ignoreList.Add(item); targetUpdateTimer = 0; }; } break; } } } protected override bool Filter(Item item) { if (!IsValidTarget(item, character)) { return false; } if (item.CurrentHull.FireSources.Count > 0) { return false; } // Don't repair items in rooms that have enemies inside. if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; } if (!Objectives.ContainsKey(item)) { if (item != character.SelectedConstruction) { float condition = item.ConditionPercentage; if (item.Repairables.All(r => condition >= r.AIRepairThreshold)) { return false; } } } if (RequireAdequateSkills) { if (item.Repairables.Any(r => !r.HasRequiredSkills(character))) { return false; } } return true; } 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()); int items = Targets.Count; bool anyFixers = otherFixers > 0; float ratio = anyFixers ? items / otherFixers : 1; var result = ratio; if (objectiveManager.CurrentOrder == this) { return Targets.Sum(t => 100 - t.ConditionPercentage) * ratio; } else { if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / HumanAIController.CountCrew() > 0.75f)) { // Enough fixers return 0; } return Targets.Sum(t => 100 - t.ConditionPercentage) * ratio; } } protected override IEnumerable GetList() => Item.ItemList; protected override AIObjective ObjectiveConstructor(Item item) => new AIObjectiveRepairItem(character, item, objectiveManager, PriorityModifier); protected override void OnObjectiveCompleted(AIObjective objective, Item target) => HumanAIController.RemoveTargets(character, target); public static bool IsValidTarget(Item item, Character character) { if (item == null) { return false; } if (item.IsFullCondition) { return false; } if (item.CurrentHull == null) { return false; } if (item.Submarine == null) { return false; } if (item.Submarine.TeamID != character.TeamID) { return false; } if (item.Repairables.None()) { return false; } if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { return false; } return true; } } }