Client LuaCsForBarotrauma
AIObjectiveLoop.cs
1 using System;
2 using System.Collections.Generic;
3 using Microsoft.Xna.Framework;
4 
5 namespace Barotrauma
6 {
11  abstract class AIObjectiveLoop<T> : AIObjective
12  {
13  public HashSet<T> Targets { get; private set; } = new HashSet<T>();
14  public Dictionary<T, AIObjective> Objectives { get; private set; } = new Dictionary<T, AIObjective>();
15  protected HashSet<T> ignoreList = new HashSet<T>();
16  private float ignoreListClearTimer;
17  protected float targetUpdateTimer;
18  protected virtual float TargetUpdateTimeMultiplier { get; } = 1;
19 
23  private float syncTimer;
24  private readonly float syncTime = 1;
25 
29  protected virtual float IgnoreListClearInterval => 0;
30 
35  public HashSet<T> ReportedTargets { get; private set; } = new HashSet<T>();
36 
37  public bool AddTarget(T target)
38  {
39  if (character.IsDead) { return false; }
40  if (ReportedTargets.Contains(target))
41  {
42  return false;
43  }
44  if (IsValidTarget(target))
45  {
46  ReportedTargets.Add(target);
47  return true;
48  }
49  return false;
50  }
51 
52  public AIObjectiveLoop(Character character, AIObjectiveManager objectiveManager, float priorityModifier, Identifier option = default)
53  : base(character, objectiveManager, priorityModifier, option) { }
54 
55  protected override void Act(float deltaTime) { }
56  protected override bool CheckObjectiveState() => false;
57  public override bool CanBeCompleted => true;
58  public override bool AbandonWhenCannotCompleteSubObjectives => false;
59  public override bool AllowSubObjectiveSorting => true;
60  protected override bool AllowWhileHandcuffed => false;
61  protected override bool AbandonIfDisallowed => false;
62 
67  public virtual bool InverseTargetPriority => false;
68  protected virtual bool ResetWhenClearingIgnoreList => true;
69  protected virtual bool ForceOrderPriority => true;
70 
71  protected virtual int MaxTargets => int.MaxValue;
72 
73  public override void Update(float deltaTime)
74  {
75  base.Update(deltaTime);
77  {
78  if (ignoreListClearTimer > IgnoreListClearInterval)
79  {
81  {
82  Reset();
83  }
84  else
85  {
86  ignoreList.Clear();
87  ignoreListClearTimer = 0;
88  }
89  }
90  else
91  {
92  ignoreListClearTimer += deltaTime;
93  }
94  }
95  if (targetUpdateTimer <= 0)
96  {
97  UpdateTargets();
98  }
99  else
100  {
101  targetUpdateTimer -= deltaTime;
102  }
103  if (syncTimer <= 0)
104  {
105  syncTimer = Math.Min(syncTime * Rand.Range(0.9f, 1.1f), targetUpdateTimer);
106  // Sync objectives, subobjectives and targets
107  foreach (var objective in Objectives)
108  {
109  var target = objective.Key;
110  if (!Targets.Contains(target))
111  {
112  subObjectives.Remove(objective.Value);
113  }
114  }
115  SyncRemovedObjectives(Objectives, GetList());
116  }
117  else
118  {
119  syncTimer -= deltaTime;
120  }
121  }
122 
123  //
127  private float CalculateTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1) * TargetUpdateTimeMultiplier;
128 
129  public override void Reset()
130  {
131  base.Reset();
132  ignoreList.Clear();
133  ignoreListClearTimer = 0;
134  UpdateTargets();
135  }
136 
137  protected override float GetPriority()
138  {
139  if (!IsAllowed)
140  {
142  return Priority;
143  }
144  // Allow the target value to be more than 100.
145  float targetPriority = GetTargetPriority();
147  {
148  targetPriority = 100 - targetPriority;
149  }
150  var currentSubObjective = CurrentSubObjective;
151  if (currentSubObjective != null && currentSubObjective.Priority > targetPriority)
152  {
153  // If the priority is higher than the target value, let's just use it.
154  // The priority calculation is more precise, but it takes into account things like distances,
155  // so it's better not to use it if it's lower than the rougher targetValue.
156  targetPriority = currentSubObjective.Priority;
157  }
158  // If the target value is less than 1% of the max value, let's just treat it as zero.
159  if (targetPriority < 1)
160  {
161  Priority = 0;
162  }
163  else
164  {
165  if (objectiveManager.IsOrder(this))
166  {
168  }
169  else
170  {
171  float max = AIObjectiveManager.LowestOrderPriority - 1;
172  if (this is AIObjectiveRescueAll rescueObjective && rescueObjective.Targets.Contains(character))
173  {
174  // Allow higher prio
176  }
177  float value = MathHelper.Clamp((CumulatedDevotion + (targetPriority * PriorityModifier)) / 100, 0, 1);
178  Priority = MathHelper.Lerp(0, max, value);
179  }
180  }
181  return Priority;
182  }
183 
184  protected void UpdateTargets()
185  {
186  CalculateTargetUpdateTimer();
187  Targets.Clear();
188  FindTargets();
190  }
191 
192  protected virtual void FindTargets()
193  {
194  foreach (T target in GetList())
195  {
196  // The bots always find targets when the objective is an order.
197  if (!objectiveManager.IsOrder(this))
198  {
199  // Battery or pump states cannot currently be reported (not implemented) and therefore we must ignore them -> the bots always know if they require attention.
200  bool ignore = this is AIObjectiveChargeBatteries || this is AIObjectivePumpWater || this is AIObjectiveFindThieves;
201  if (!ignore && !ReportedTargets.Contains(target)) { continue; }
202  }
203  if (!IsValidTarget(target)) { continue; }
204  if (!ignoreList.Contains(target))
205  {
206  Targets.Add(target);
207  if (Targets.Count > MaxTargets)
208  {
209  break;
210  }
211  }
212  }
213  }
214 
215  protected virtual void CreateObjectives()
216  {
217  foreach (T target in Targets)
218  {
219  if (ignoreList.Contains(target)) { continue; }
220  if (!Objectives.TryGetValue(target, out AIObjective objective))
221  {
222  objective = ObjectiveConstructor(target);
223  Objectives.Add(target, objective);
224  if (!subObjectives.Contains(objective))
225  {
226  subObjectives.Add(objective);
227  }
228  objective.Completed += () =>
229  {
230  Objectives.Remove(target);
231  OnObjectiveCompleted(objective, target);
232  };
233  objective.Abandoned += () =>
234  {
235  Objectives.Remove(target);
236  ignoreList.Add(target);
237  targetUpdateTimer = Math.Min(0.1f, targetUpdateTimer);
238  };
239  }
240  }
241  }
242 
243  protected abstract void OnObjectiveCompleted(AIObjective objective, T target);
244 
248  protected abstract IEnumerable<T> GetList();
249 
254  protected abstract float GetTargetPriority();
255 
256  protected abstract AIObjective ObjectiveConstructor(T target);
257  protected abstract bool IsValidTarget(T target);
258  }
259 }
float Priority
Final priority value after all calculations.
An objective that creates specific kinds of subobjectives for specific types of targets,...
AIObjectiveLoop(Character character, AIObjectiveManager objectiveManager, float priorityModifier, Identifier option=default)
virtual float TargetUpdateTimeMultiplier
override bool AllowSubObjectiveSorting
abstract AIObjective ObjectiveConstructor(T target)
virtual float IgnoreListClearInterval
By default, doesn't clear the list automatically
override bool AbandonWhenCannotCompleteSubObjectives
override float GetPriority()
override bool AllowWhileHandcuffed
virtual bool ResetWhenClearingIgnoreList
HashSet< T > ReportedTargets
Contains targets that anyone in the same crew has reported about. Used for automatic the target has t...
override bool AbandonIfDisallowed
abstract void OnObjectiveCompleted(AIObjective objective, T target)
Dictionary< T, AIObjective > Objectives
abstract float GetTargetPriority()
Returns a priority value based on the current targets (e.g. high prio when there's lots of severe fir...
override void Update(float deltaTime)
abstract IEnumerable< T > GetList()
List of all possible items of the specified type. Used for filtering the removed objectives.
virtual bool InverseTargetPriority
Makes the priority inversely proportional to the value returned by GetTargetPriority....
override void Act(float deltaTime)
abstract bool IsValidTarget(T target)
override bool CheckObjectiveState()
Should return whether the objective is completed or not.
const float EmergencyObjectivePriority
Priority of objectives such as finding safety, rescuing someone in a critical state or defending agai...
const float LowestOrderPriority
Maximum priority of an order given to the character (rightmost order in the crew list)
float GetOrderPriority(AIObjective objective)
bool IsOrder(AIObjective objective)