Client LuaCsForBarotrauma
AIObjectiveReturn.cs
2 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
4 
5 namespace Barotrauma
6 {
8  {
9  public override Identifier Identifier { get; set; } = "return".ToIdentifier();
10  public Submarine ReturnTarget { get; }
11 
12  private AIObjectiveGoTo moveInsideObjective, moveOutsideObjective;
13  private bool usingEscapeBehavior, isSteeringThroughGap;
14 
15  protected override bool AllowOutsideSubmarine => true;
16  protected override bool AllowInAnySub => true;
17 
18  public AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier = 1.0f) : base(character, objectiveManager, priorityModifier)
19  {
20  ReturnTarget = GetReturnTarget(Submarine.MainSubs) ?? GetReturnTarget(Submarine.Loaded);
21  if (ReturnTarget == null)
22  {
23  DebugConsole.AddSafeError("Error with a Return objective: no suitable return target found");
24  Abandon = true;
25  }
26 
27  Submarine GetReturnTarget(IEnumerable<Submarine> subs)
28  {
29  var requiredTeamID = orderGiver?.TeamID ?? character?.TeamID;
30  Submarine returnTarget = null;
31  foreach (var sub in subs)
32  {
33  if (sub == null) { continue; }
34  if (sub.TeamID != requiredTeamID) { continue; }
35  returnTarget = sub;
36  break;
37  }
38  return returnTarget;
39  }
40  }
41 
42  protected override float GetPriority()
43  {
44  if (!Abandon && !IsCompleted && objectiveManager.IsOrder(this))
45  {
47  }
48  else
49  {
51  }
52  return Priority;
53  }
54 
55  protected override void Act(float deltaTime)
56  {
57  if (ReturnTarget == null)
58  {
59  Abandon = true;
60  return;
61  }
62  bool shouldUseEscapeBehavior = false;
63  if (character.CurrentHull != null || isSteeringThroughGap)
64  {
66  {
67  // Character is on another sub that is not connected to the target sub, use the escape behavior to get them out
68  shouldUseEscapeBehavior = true;
69  if (!usingEscapeBehavior)
70  {
72  }
73  isSteeringThroughGap = HumanAIController.Escape(deltaTime);
74  if (!isSteeringThroughGap && (HumanAIController.EscapeTarget == null || HumanAIController.IsCurrentPathUnreachable))
75  {
76  Abandon = true;
77  }
78  }
79  else if (character.Submarine != ReturnTarget)
80  {
81  // Character is on another sub that is connected to the target sub, create a Go To objective to reach the target sub
82  if (moveInsideObjective == null)
83  {
84  Hull targetHull = null;
85  foreach (var d in ReturnTarget.ConnectedDockingPorts.Values)
86  {
87  if (!d.Docked) { continue; }
88  if (d.DockingTarget == null) { continue; }
89  if (d.DockingTarget.Item.Submarine != character.Submarine) { continue; }
90  targetHull = d.Item.CurrentHull;
91  break;
92  }
93  if (targetHull != null && !targetHull.IsAirlock)
94  {
95  // Target the closest airlock
96  float closestDist = 0;
97  Hull airlock = null;
98  foreach (Hull hull in Hull.HullList)
99  {
100  if (hull.Submarine != targetHull.Submarine) { continue; }
101  if (!hull.IsAirlock) { continue; }
102  float dist = Vector2.DistanceSquared(targetHull.Position, hull.Position);
103  if (airlock == null || closestDist <= 0 || dist < closestDist)
104  {
105  airlock = hull;
106  closestDist = dist;
107  }
108 
109  }
110  if (airlock != null)
111  {
112  targetHull = airlock;
113  }
114  }
115  if (targetHull != null)
116  {
117  RemoveSubObjective(ref moveOutsideObjective);
118  TryAddSubObjective(ref moveInsideObjective,
119  constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
120  {
121  AllowGoingOutside = true,
122  endNodeFilter = n => n.Waypoint.Submarine == targetHull.Submarine
123  },
124  onCompleted: () => RemoveSubObjective(ref moveInsideObjective),
125  onAbandon: () => Abandon = true);
126  }
127  else
128  {
129 #if DEBUG
130  DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveInsideObjective'");
131 #endif
132  }
133  }
134  }
135  else
136  {
137  // Character is on the target sub, the objective is completed
138  IsCompleted = true;
139  }
140  }
141  else if (!isSteeringThroughGap && moveOutsideObjective == null)
142  {
143  Hull targetHull = null;
144  float targetDistanceSquared = float.MaxValue;
145  bool targetIsAirlock = false;
146  foreach (var hull in ReturnTarget.GetHulls(false))
147  {
148  bool hullIsAirlock = hull.IsAirlock;
149  if(hullIsAirlock || (!targetIsAirlock && hull.LeadsOutside(character)))
150  {
151  float distanceSquared = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
152  if (targetHull == null || distanceSquared < targetDistanceSquared)
153  {
154  targetHull = hull;
155  targetDistanceSquared = distanceSquared;
156  targetIsAirlock = hullIsAirlock;
157  }
158  }
159  }
160  if (targetHull != null)
161  {
162  RemoveSubObjective(ref moveInsideObjective);
163  TryAddSubObjective(ref moveOutsideObjective,
164  constructor: () => new AIObjectiveGoTo(targetHull, character, objectiveManager)
165  {
166  AllowGoingOutside = true
167  },
168  onCompleted: () => RemoveSubObjective(ref moveOutsideObjective),
169  onAbandon: () => Abandon = true);
170  }
171  else
172  {
173 #if DEBUG
174  DebugConsole.ThrowError("Error with a Return objective: no suitable target for 'moveOutsideObjective'");
175 #endif
176  }
177  }
178  usingEscapeBehavior = shouldUseEscapeBehavior;
179  }
180 
181  protected override bool CheckObjectiveSpecific()
182  {
183  if (IsCompleted)
184  {
185  return true;
186  }
187  if (ReturnTarget == null)
188  {
189  Abandon = true;
190  return false;
191  }
193  {
194  IsCompleted = true;
195  }
196  return IsCompleted;
197  }
198 
199  public override void Reset()
200  {
201  base.Reset();
202  moveInsideObjective = null;
203  moveOutsideObjective = null;
204  usingEscapeBehavior = false;
205  isSteeringThroughGap = false;
207  }
208 
209  protected override void OnAbandon()
210  {
211  base.OnAbandon();
214  {
215  string msg = TextManager.Get("dialogcannotreturn").Value;
216  if (!msg.IsNullOrEmpty())
217  {
218  character.Speak(msg, identifier: "dialogcannotreturn".ToIdentifier(), minDurationBetweenSimilar: 5.0f);
219  }
220  }
221  }
222  }
223 }
float Priority
Final priority value after all calculations.
const float LowestOrderPriority
Maximum priority of an order given to the character (rightmost order in the crew list)
AIObjective CurrentObjective
Includes orders.
AIObjective?? CurrentOrder
The AIObjective in CurrentOrders with the highest AIObjective.Priority
float GetOrderPriority(AIObjective objective)
bool IsOrder(AIObjective objective)
override bool CheckObjectiveSpecific()
Should return whether the objective is completed or not.
AIObjectiveReturn(Character character, Character orderGiver, AIObjectiveManager objectiveManager, float priorityModifier=1.0f)
override void Act(float deltaTime)
override Identifier Identifier
void Speak(string message, ChatMessageType? messageType=null, float delay=0.0f, Identifier identifier=default, float minDurationBetweenSimilar=0.0f)
virtual Vector2 WorldPosition
Definition: Entity.cs:49
Submarine Submarine
Definition: Entity.cs:53
static readonly List< Hull > HullList
override bool Escape(float deltaTime)
readonly Dictionary< Submarine, DockingPort > ConnectedDockingPorts
Submarine(SubmarineInfo info, bool showErrorMessages=true, Func< Submarine, List< MapEntity >> loadEntities=null, IdRemap linkedRemap=null)
bool IsConnectedTo(Submarine otherSub)
Returns true if the sub is same as the other, or connected to it via docking ports.
List< Hull > GetHulls(bool alsoFromConnectedSubs)