Client LuaCsForBarotrauma
AIObjectiveRepairItems.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
6 using Microsoft.Xna.Framework;
7 
8 namespace Barotrauma
9 {
11  {
12  public override Identifier Identifier { get; set; } = "repair items".ToIdentifier();
13 
18 
19  public Item PrioritizedItem { get; private set; }
20 
21  public override bool AllowMultipleInstances => true;
22  protected override bool AllowInFriendlySubs => true;
23 
24  public const float RequiredSuccessFactor = 0.4f;
25 
26  public override bool IsDuplicate<T>(T otherObjective) => otherObjective is AIObjectiveRepairItems repairObjective && objectiveManager.IsOrder(repairObjective) == objectiveManager.IsOrder(this);
27 
28  public AIObjectiveRepairItems(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1, Item prioritizedItem = null)
29  : base(character, objectiveManager, priorityModifier)
30  {
31  PrioritizedItem = prioritizedItem;
32  }
33 
34  protected override void CreateObjectives()
35  {
36  foreach (var item in Targets)
37  {
38  foreach (Repairable repairable in item.Repairables)
39  {
40  if (!Objectives.TryGetValue(item, out AIObjective objective))
41  {
42  objective = ObjectiveConstructor(item);
43  Objectives.Add(item, objective);
44  if (!subObjectives.Contains(objective))
45  {
46  subObjectives.Add(objective);
47  }
48  objective.Completed += () =>
49  {
50  Objectives.Remove(item);
51  OnObjectiveCompleted(objective, item);
52  };
53  objective.Abandoned += () =>
54  {
55  Objectives.Remove(item);
56  ignoreList.Add(item);
57  targetUpdateTimer = Math.Min(0.1f, targetUpdateTimer);
58  };
59  }
60  break;
61  }
62  }
63  }
64 
65  protected override bool IsValidTarget(Item item)
66  {
67  if (!ViableForRepair(item, character, HumanAIController)) { return false; };
68  if (!Objectives.ContainsKey(item))
69  {
70  if (item != character.SelectedItem)
71  {
72  if (NearlyFullCondition(item)) { return false; }
73  }
74  }
75  if (!RelevantSkill.IsEmpty)
76  {
77  if (item.Repairables.None(r => r.RequiredSkills.Any(s => s.Identifier == RelevantSkill))) { return false; }
78  }
79  return !HumanAIController.IsItemRepairedByAnother(item, out _);
80  }
81 
82  public static bool ViableForRepair(Item item, Character character, HumanAIController humanAIController)
83  {
84  if (!IsValidTarget(item, character)) { return false; }
85  if (item.CurrentHull == null) { return true; }
86  if (item.CurrentHull.FireSources.Count > 0) { return false; }
87  // Don't repair items in rooms that have enemies inside.
88  if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !humanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
89  return true;
90  }
91 
92  public static bool NearlyFullCondition(Item item)
93  {
94  return item.Repairables.All(r => !r.IsBelowRepairThreshold);
95  }
96 
97  protected override float GetTargetPriority()
98  {
99  var selectedItem = character.SelectedItem;
100  if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100)
101  {
102  // Don't stop fixing until completely done
103  return 100;
104  }
105  int otherFixers = HumanAIController.CountBotsInTheCrew(c => c != HumanAIController && c.ObjectiveManager.IsCurrentObjective<AIObjectiveRepairItems>());
106  int items = Targets.Count;
107  if (items == 0)
108  {
109  return 0;
110  }
111  bool anyFixers = otherFixers > 0;
112  float ratio = anyFixers ? items / (float)otherFixers : 1;
113  if (objectiveManager.IsOrder(this))
114  {
115  return Targets.Sum(t => 100 - t.ConditionPercentage);
116  }
117  else
118  {
119  if (anyFixers && (ratio <= 1 || otherFixers > 5 || otherFixers / (float)HumanAIController.CountBotsInTheCrew() > 0.75f))
120  {
121  // Enough fixers
122  return 0;
123  }
124  return Targets.Sum(t => GetTargetPriority(t, character, RequiredSuccessFactor)) * ratio;
125  }
126  }
127 
128  public static float GetTargetPriority(Item item, Character character, float requiredSuccessFactor = 0)
129  {
130  float damagePriority = MathHelper.Lerp(1, 0, item.Condition / item.MaxCondition);
131  float successFactor = MathHelper.Lerp(0, 1, item.Repairables.Average(r => r.DegreeOfSuccess(character)));
132  if (successFactor < requiredSuccessFactor)
133  {
134  return 0;
135  }
136  return MathHelper.Lerp(0, 100, MathHelper.Clamp(damagePriority * successFactor, 0, 1));
137  }
138 
139  protected override IEnumerable<Item> GetList() => Item.RepairableItems;
140 
141  protected override AIObjective ObjectiveConstructor(Item item)
142  => new AIObjectiveRepairItem(character, item, objectiveManager, priorityModifier: PriorityModifier, isPriority: item == PrioritizedItem);
143 
144  protected override void OnObjectiveCompleted(AIObjective objective, Item target)
145  => HumanAIController.RemoveTargets<AIObjectiveRepairItems, Item>(character, target);
146 
147  public static bool IsValidTarget(Item item, Character character)
148  {
149  if (item == null) { return false; }
150  if (item.IgnoreByAI(character)) { return false; }
151  if (!item.IsInteractable(character)) { return false; }
152  if (item.IsFullCondition) { return false; }
153  if (item.Submarine == null || character.Submarine == null) { return false; }
154  if (item.IsClaimedByBallastFlora) { return false; }
155  //player crew ignores items in outposts
156  if (character.IsOnPlayerTeam && item.Submarine.Info.IsOutpost) { return false; }
157  if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { return false; }
158  if (item.Repairables.None()) { return false; }
159 
160  System.Diagnostics.Debug.Assert(item.Repairables.Any(), "Invalid target in AIObjectiveRepairItems - the objective should only be checking items that have a Repairable component (Item.RepairableItems)");
161 
162  return true;
163  }
164  }
165 }
An objective that creates specific kinds of subobjectives for specific types of targets,...
Dictionary< T, AIObjective > Objectives
static float GetTargetPriority(Item item, Character character, float requiredSuccessFactor=0)
override bool IsDuplicate< T >(T otherObjective)
AIObjectiveRepairItems(Character character, AIObjectiveManager objectiveManager, float priorityModifier=1, Item prioritizedItem=null)
override float GetTargetPriority()
Returns a priority value based on the current targets (e.g. high prio when there's lots of severe fir...
static bool NearlyFullCondition(Item item)
override bool IsValidTarget(Item item)
static bool IsValidTarget(Item item, Character character)
override void OnObjectiveCompleted(AIObjective objective, Item target)
override AIObjective ObjectiveConstructor(Item item)
override IEnumerable< Item > GetList()
List of all possible items of the specified type. Used for filtering the removed objectives.
static bool ViableForRepair(Item item, Character character, HumanAIController humanAIController)
Identifier RelevantSkill
If set, only fix items where required skill matches this.
Submarine Submarine
Definition: Entity.cs:53
static int CountBotsInTheCrew(Character character, Func< HumanAIController, bool > predicate=null)
static bool IsActive(Character c)
static bool IsFriendly(Character me, Character other, bool onlySameTeam=false)
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
bool IgnoreByAI(Character character)
static IReadOnlyCollection< Item > RepairableItems
Items that have one more more Repairable component
bool IsEntityFoundOnThisSub(MapEntity entity, bool includingConnectedSubs, bool allowDifferentTeam=false, bool allowDifferentType=false)