Client LuaCsForBarotrauma
AIObjectiveOperateItem.cs
2 using Microsoft.Xna.Framework;
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 namespace Barotrauma
8 {
10  {
11  public override Identifier Identifier { get; set; } = "operate item".ToIdentifier();
12  public override string DebugTag => $"{Identifier} {component.Name}";
13 
14  public override bool AllowAutomaticItemUnequipping => true;
15  public override bool AllowMultipleInstances => true;
16  protected override bool AllowInAnySub => true;
17  protected override bool AllowWhileHandcuffed => false;
18  public override bool PrioritizeIfSubObjectivesActive => component != null && (component is Reactor || component is Turret);
19 
20  private readonly ItemComponent component, controller;
21  private readonly Entity operateTarget;
22  private readonly bool requireEquip;
23  private readonly bool useController;
24  private AIObjectiveGoTo goToObjective;
25  private AIObjectiveGetItem getItemObjective;
26 
30  public Func<PathNode, bool> EndNodeFilter;
31 
32  public bool Override { get; init; } = true;
33 
37  public bool Repeat { get; init; }
38 
39  public override bool CanBeCompleted => base.CanBeCompleted && (!useController || controller != null);
40 
41  public override bool IsDuplicate<T>(T otherObjective) => base.IsDuplicate(otherObjective) && otherObjective is AIObjectiveOperateItem operateObjective && operateObjective.component == component;
42 
43  public Entity OperateTarget => operateTarget;
44  public ItemComponent Component => component;
45 
46  public ItemComponent GetTarget() => useController ? controller : component;
47 
48  public Func<bool> completionCondition;
49  private bool isDoneOperating;
50 
51  public float? OverridePriority = null;
52 
53  protected override float GetPriority()
54  {
55  bool isOrder = objectiveManager.IsOrder(this);
56  if (!IsAllowed)
57  {
59  return Priority;
60  }
61  if (!isOrder && component.Item.ConditionPercentage <= 0)
62  {
63  Priority = 0;
64  }
65  else
66  {
67  if (OverridePriority.HasValue)
68  {
69  Priority = OverridePriority.Value;
70  }
71  else if (isOrder)
72  {
74  }
75  ItemComponent target = GetTarget();
76  Item targetItem = target?.Item;
77  if (targetItem == null)
78  {
79 #if DEBUG
80  DebugConsole.ThrowError("Item or component of AI Objective Operate item was null. This shouldn't happen.");
81 #endif
82  Abandon = true;
83  Priority = 0;
84  return Priority;
85  }
86  else if (targetItem.IsClaimedByBallastFlora)
87  {
88  Priority = 0;
89  return Priority;
90  }
91  var reactor = component?.Item.GetComponent<Reactor>();
92  if (reactor != null)
93  {
94  if (!isOrder)
95  {
96  if (reactor.LastUserWasPlayer && character.TeamID != CharacterTeamType.FriendlyNPC)
97  {
98  // The reactor was previously operated by a player -> ignore.
99  Priority = 0;
100  return Priority;
101  }
102  }
103  switch (Option.Value.ToLowerInvariant())
104  {
105  case "shutdown":
106  if (!reactor.PowerOn)
107  {
108  Priority = 0;
109  return Priority;
110  }
111  break;
112  case "powerup":
113  // Check that we don't already have another order that is targeting the same item.
114  // Without this the autonomous objective will tell the bot to turn the reactor on again.
115  if (IsAnotherOrderTargetingSameItem(objectiveManager.ForcedOrder) || objectiveManager.CurrentOrders.Any(o => IsAnotherOrderTargetingSameItem(o.Objective)))
116  {
117  Priority = 0;
118  return Priority;
119  }
120  bool IsAnotherOrderTargetingSameItem(AIObjective objective)
121  {
122  return objective is AIObjectiveOperateItem operateObjective && operateObjective != this && operateObjective.GetTarget() == target && operateObjective.Option != Option;
123  }
124  break;
125  }
126  }
127  else if (!isOrder)
128  {
129  var steering = component?.Item.GetComponent<Steering>();
130  if (steering != null && (steering.AutoPilot || HumanAIController.IsTrueForAnyCrewMember(c => c != character && c.IsCaptain, onlyActive: true, onlyConnectedSubs: true)))
131  {
132  // Ignore if already set to autopilot or if there's a captain onboard
133  Priority = 0;
134  return Priority;
135  }
136  }
137  if (targetItem.CurrentHull == null ||
138  targetItem.Submarine != character.Submarine && !isOrder ||
139  targetItem.CurrentHull.FireSources.Any() ||
141  Character.CharacterList.Any(c => c.CurrentHull == targetItem.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))
142  || component.Item.IgnoreByAI(character) || useController && controller.Item.IgnoreByAI(character))
143  {
144  Priority = 0;
145  }
146  else
147  {
148  if (isOrder)
149  {
150  float max = objectiveManager.GetOrderPriority(this);
151  float value = CumulatedDevotion + (max * PriorityModifier);
152  Priority = MathHelper.Clamp(value, 0, max);
153  }
154  else if (!OverridePriority.HasValue)
155  {
157  float max = AIObjectiveManager.LowestOrderPriority - 1;
158  if (reactor != null && reactor.PowerOn && reactor.FissionRate > 1 && reactor.AutoTemp && Option == "powerup")
159  {
160  // Already on, no need to operate.
161  value = 0;
162  }
163  Priority = MathHelper.Clamp(value, 0, max);
164  }
165  }
166  }
167  return Priority;
168  }
169 
171  Entity operateTarget = null, bool useController = false, ItemComponent controller = null, float priorityModifier = 1)
172  : base(character, objectiveManager, priorityModifier, option)
173  {
174  component = item ?? throw new ArgumentNullException("item", "Attempted to create an AIObjectiveOperateItem with a null target.");
175  this.requireEquip = requireEquip;
176  this.operateTarget = operateTarget;
177  this.useController = useController;
178  if (useController) { this.controller = controller ?? component?.Item?.FindController(); }
179  var target = GetTarget();
180  if (target == null)
181  {
182  Abandon = true;
183 #if DEBUG
184  throw new Exception("target null");
185 #endif
186  }
187  else if (!target.Item.IsInteractable(character))
188  {
189  Abandon = true;
190  }
191  }
192 
193  protected override void Act(float deltaTime)
194  {
195  if (character.LockHands)
196  {
197  Abandon = true;
198  return;
199  }
200  ItemComponent target = GetTarget();
201  if (useController && controller == null)
202  {
204  {
205  character.Speak(TextManager.GetWithVariable("DialogCantFindController", "[item]", component.Item.Name).Value, delay: 2.0f, identifier: "cantfindcontroller".ToIdentifier(), minDurationBetweenSimilar: 30.0f);
206  }
207  Abandon = true;
208  return;
209  }
210  if (operateTarget != null)
211  {
212  if (HumanAIController.IsTrueForAnyBotInTheCrew(other => other != HumanAIController && other.ObjectiveManager.GetActiveObjective() is AIObjectiveOperateItem operateObjective && operateObjective.operateTarget == operateTarget))
213  {
214  // Another crew member is already targeting this entity (leak).
215  Abandon = true;
216  return;
217  }
218  }
219 
220  //the character shouldn't be grabbing anyone if it's trying to operate an item
222 
223  if (target.CanBeSelected)
224  {
225  if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
226  {
227  if (target.Item.GetComponent<Controller>() is not Controller { ControlCharacterPose: true })
228  {
230  }
231  else
232  {
234  }
235  if (character.SelectedItem != target.Item && character.SelectedSecondaryItem != target.Item)
236  {
237  target.Item.TryInteract(character, forceSelectKey: true);
238  }
239  if (component.CrewAIOperate(deltaTime, character, this))
240  {
241  isDoneOperating = completionCondition == null || completionCondition();
242  }
243  }
244  else
245  {
246  TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(target.Item, character, objectiveManager, closeEnough: 50)
247  {
248  TargetName = target.Item.Name,
249  endNodeFilter = EndNodeFilter ?? AIObjectiveGetItem.CreateEndNodeFilter(target.Item)
250  },
251  onAbandon: () => Abandon = true,
252  onCompleted: () => RemoveSubObjective(ref goToObjective));
253  }
254  }
255  else
256  {
257  if (component.Item.GetComponent<Pickable>() == null)
258  {
259  //controller/target can't be selected and the item cannot be picked -> objective can't be completed
260  Abandon = true;
261  return;
262  }
263  else if (!character.Inventory.Contains(component.Item))
264  {
265  TryAddSubObjective(ref getItemObjective, () => new AIObjectiveGetItem(character, component.Item, objectiveManager, equip: true),
266  onAbandon: () => Abandon = true,
267  onCompleted: () => RemoveSubObjective(ref getItemObjective));
268  }
269  else
270  {
271  if (requireEquip && !character.HasEquippedItem(component.Item))
272  {
273  //the item has to be equipped before using it if it's holdable
274  var holdable = component.Item.GetComponent<Holdable>();
275  if (holdable == null)
276  {
277 #if DEBUG
278  DebugConsole.ThrowError($"{character.Name}: AIObjectiveOperateItem failed - equipping item " + component.Item + " is required but the item has no Holdable component");
279 #endif
280  return;
281  }
282  for (int i = 0; i < character.Inventory.Capacity; i++)
283  {
284  if (character.Inventory.SlotTypes[i] == InvSlotType.Any || !holdable.AllowedSlots.Any(s => s.HasFlag(character.Inventory.SlotTypes[i])))
285  {
286  continue;
287  }
288  //equip slot already taken
289  var existingItem = character.Inventory.GetItemAt(i);
290  if (existingItem != null)
291  {
292  //try to put the item in an Any slot, and drop it if that fails
293  if (!existingItem.AllowedSlots.Contains(InvSlotType.Any) ||
294  !character.Inventory.TryPutItem(existingItem, character, new List<InvSlotType>() { InvSlotType.Any }))
295  {
296  existingItem.Drop(character);
297  }
298  }
299  if (character.Inventory.TryPutItem(component.Item, i, true, false, character))
300  {
301  component.Item.Equip(character);
302  break;
303  }
304  }
305  return;
306  }
307  if (component.CrewAIOperate(deltaTime, character, this))
308  {
309  isDoneOperating = completionCondition == null || completionCondition();
310  }
311  }
312  }
313  }
314 
315  protected override bool CheckObjectiveSpecific() => isDoneOperating && !Repeat;
316 
317  public override void Reset()
318  {
319  base.Reset();
320  goToObjective = null;
321  getItemObjective = null;
322  }
323  }
324 }
void FaceTarget(ISpatialEntity target)
float Priority
Final priority value after all calculations.
AIObjective GetActiveObjective()
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)
override bool CheckObjectiveSpecific()
Should return whether the objective is completed or not.
override bool IsDuplicate< T >(T otherObjective)
Func< PathNode, bool > EndNodeFilter
If undefined, a default filter will be used.
override void Act(float deltaTime)
bool Repeat
When true, the operate objective is never completed, unless it's abandoned.
AIObjectiveOperateItem(ItemComponent item, Character character, AIObjectiveManager objectiveManager, Identifier option, bool requireEquip, Entity operateTarget=null, bool useController=false, ItemComponent controller=null, float priorityModifier=1)
void Speak(string message, ChatMessageType? messageType=null, float delay=0.0f, Identifier identifier=default, float minDurationBetweenSimilar=0.0f)
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
Item SelectedSecondaryItem
The secondary selected item. It's an item other than a device (see SelectedItem), e....
override bool TryPutItem(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true, bool ignoreCondition=false)
If there is room, puts the item in the inventory and returns true, otherwise returns false
Submarine Submarine
Definition: Entity.cs:53
static bool IsTrueForAnyBotInTheCrew(Character character, Func< HumanAIController, bool > predicate)
bool IsItemOperatedByAnother(ItemComponent target, out Character other)
static bool IsActive(Character c)
bool IsTrueForAnyCrewMember(Func< Character, bool > predicate, bool onlyActive=true, bool onlyConnectedSubs=false)
Including the player characters in the same team.
static bool IsFriendly(Character me, Character other, bool onlySameTeam=false)
bool Contains(Item item)
Is the item contained in this inventory. Does not recursively check items inside items.
Item GetItemAt(int index)
Get the item stored in the specified inventory slot. If the slot contains a stack of items,...
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
bool TryInteract(Character user, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceUseKey=false)
The base class for components holding the different functionalities of the item