Client LuaCsForBarotrauma
AIObjectiveContainItem.cs
3 using System;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
6 using System.Linq;
7 using Microsoft.Xna.Framework;
8 
9 namespace Barotrauma
10 {
12  {
13  public override Identifier Identifier { get; set; } = "contain item".ToIdentifier();
14  protected override bool AllowWhileHandcuffed => false;
15 
16  public Func<Item, float> GetItemPriority;
17 
18  public ImmutableHashSet<Identifier> ignoredContainerIdentifiers;
19  public bool checkInventory = true;
20 
21  //if the item can't be found, spawn it in the character's inventory (used by outpost NPCs and in some cases also enemy NPCs, like pirates)
22  private readonly bool spawnItemIfNotFound;
23 
24  //can either be a tag or an identifier
25  public readonly ImmutableHashSet<Identifier> itemIdentifiers;
26  public readonly ItemContainer container;
27  private readonly Item item;
28  public Item ItemToContain { get; private set; }
29 
30  public int? TargetSlot;
31 
32  private AIObjectiveGetItem getItemObjective;
33  private AIObjectiveGoTo goToObjective;
34 
35  private readonly HashSet<Item> containedItems = new HashSet<Item>();
36 
37  public bool AllowToFindDivingGear { get; set; } = true;
38  public bool AllowDangerousPressure { get; set; }
39  public float ConditionLevel { get; set; } = 1;
40  public bool Equip { get; set; }
41  public bool RemoveEmpty { get; set; } = true;
42  public bool RemoveExisting { get; set; }
46  public bool RemoveExistingWhenNecessary { get; set; }
47  public Func<Item, bool> RemoveExistingPredicate { get; set; }
48  public int? RemoveMax { get; set; }
49 
50  public bool MoveWholeStack { get; set; }
51 
52  private int _itemCount = 1;
53  public int ItemCount
54  {
55  get { return _itemCount; }
56  set
57  {
58  _itemCount = Math.Max(value, 1);
59  }
60  }
61 
63  : base(character, objectiveManager, priorityModifier)
64  {
65  this.container = container;
66  this.item = item;
67  }
68 
69  public AIObjectiveContainItem(Character character, Identifier itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
70  : this(character, itemIdentifier.ToEnumerable().ToImmutableHashSet(), container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
71 
72  public AIObjectiveContainItem(Character character, ImmutableHashSet<Identifier> itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
73  : base(character, objectiveManager, priorityModifier)
74  {
75  this.itemIdentifiers = itemIdentifiers;
76  this.spawnItemIfNotFound = spawnItemIfNotFound;
77  this.container = container;
78  }
79 
80  protected override bool CheckObjectiveSpecific()
81  {
82  if (IsCompleted) { return true; }
83  if (container?.Item == null || !container.Item.HasAccess(character))
84  {
85  Abandon = true;
86  return false;
87  }
88  if (item != null)
89  {
90  return container.Inventory.Contains(item);
91  }
92  else
93  {
94  return CountItems();
95  }
96  }
97 
98  private bool CountItems()
99  {
100  int containedItemCount = 0;
101  foreach (Item it in container.Inventory.AllItems)
102  {
103  if (CheckItem(it) && IsInTargetSlot(it))
104  {
105  containedItemCount++;
106  }
107  }
108  return containedItemCount >= ItemCount;
109  }
110 
111  private bool CheckItem(Item item)
112  {
113  return item.HasIdentifierOrTags(itemIdentifiers) && item.ConditionPercentage >= ConditionLevel && item.HasAccess(character) && container.ShouldBeContained(item, out _);
114  }
115 
116  protected override void Act(float deltaTime)
117  {
118  if (container?.Item == null)
119  {
120  Abandon = true;
121  return;
122  }
123  ItemToContain = item ?? character.Inventory.FindItem(it =>
124  CheckItem(it) &&
125  //ignore items already in the container, unless we're trying to place to a specific slot, and the item's not in it
126  (it.Container != container.Item || (TargetSlot.HasValue && it.Container.OwnInventory.FindIndex(it) != TargetSlot)),
127  recursive: true);
128  if (ItemToContain != null)
129  {
130  if (!character.CanInteractWith(ItemToContain, checkLinked: false))
131  {
132  Abandon = true;
133  return;
134  }
135  if (character.CanInteractWith(container.Item, checkLinked: false))
136  {
137  static bool CanBePut(Inventory inventory, int? targetSlot, Item itemToContain)
138  {
139  if (targetSlot.HasValue)
140  {
141  return inventory.CanBePutInSlot(itemToContain, targetSlot.Value);
142  }
143  else
144  {
145  return inventory.CanBePut(itemToContain);
146  }
147  }
148 
150  {
152  }
153  else if (RemoveEmpty)
154  {
156  }
157  Inventory originalInventory = ItemToContain.ParentInventory;
158  var slots = originalInventory?.FindIndices(ItemToContain);
159 
160  bool TryPutItem(Inventory inventory, int? targetSlot, Item itemToContain)
161  {
162  if (targetSlot.HasValue)
163  {
164  return inventory.TryPutItem(itemToContain, targetSlot.Value, allowSwapping: false, allowCombine: false, user: character);
165  }
166  else
167  {
168  return inventory.TryPutItem(itemToContain, user: character);
169  }
170  }
171 
172  if (TryPutItem(container.Inventory, TargetSlot, ItemToContain))
173  {
174  if (MoveWholeStack && slots != null)
175  {
176  foreach (int slot in slots)
177  {
178  foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
179  {
180  TryPutItem(container.Inventory, TargetSlot, item);
181  }
182  }
183  }
184  IsCompleted = item != null || CountItems();
185  }
186  else
187  {
189  {
191  }
192  Abandon = true;
193  }
194  }
195  else
196  {
197  TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager, getDivingGearIfNeeded: AllowToFindDivingGear)
198  {
199  TargetName = container.Item.Name,
200  AbortCondition = obj =>
201  container?.Item == null || container.Item.Removed || !container.Item.HasAccess(character) ||
202  (container.Item.RootContainer?.OwnInventory?.Locked ?? false) ||
203  ItemToContain == null || ItemToContain.Removed ||
204  !ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
205  SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>(),
206  endNodeFilter = n => Vector2.DistanceSquared(n.Waypoint.WorldPosition, container.Item.WorldPosition) <= MathUtils.Pow2(AIObjectiveGetItem.MaxReach)
207  },
208  onAbandon: () => Abandon = true,
209  onCompleted: () => RemoveSubObjective(ref goToObjective));
210  }
211  }
212  else
213  {
214  if (character.Submarine == null)
215  {
216  Abandon = true;
217  }
218  else
219  {
220  // No matching items in the inventory, try to get an item
221  TryAddSubObjective(ref getItemObjective, () =>
222  new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, equip: Equip, checkInventory: checkInventory, spawnItemIfNotFound: spawnItemIfNotFound)
223  {
226  ignoredItems = containedItems,
229  TargetCondition = ConditionLevel,
230  ItemFilter = (Item potentialItem) =>
231  {
232  return (RemoveEmpty ? container.CanBeContained(potentialItem) : container.Inventory.CanBePut(potentialItem)) && container.ShouldBeContained(potentialItem, out _);
233  },
235  TakeWholeStack = MoveWholeStack
236  }, onAbandon: () =>
237  {
238  Abandon = true;
239  }, onCompleted: () =>
240  {
241  if (getItemObjective?.TargetItem != null)
242  {
243  containedItems.Add(getItemObjective.TargetItem);
244  }
245  RemoveSubObjective(ref getItemObjective);
246  });
247  }
248  }
249  }
250 
251  public bool IsInTargetSlot(Item item)
252  {
253  if (TargetSlot == null) { return true; }
254  if (container?.Inventory is ItemInventory inventory)
255  {
256  return inventory.IsInSlot(item, (int)TargetSlot);
257  }
258  return false;
259  }
260 
261  public override void Reset()
262  {
263  base.Reset();
264  getItemObjective = null;
265  goToObjective = null;
266  containedItems.Clear();
267  }
268  }
269 }
void UnequipContainedItems(Item parentItem, Func< Item, bool > predicate=null, bool avoidDroppingInSea=true, int? unequipMax=null)
void UnequipEmptyItems(Item parentItem, bool avoidDroppingInSea=true)
AIObjectiveContainItem(Character character, Item item, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier=1)
AIObjectiveContainItem(Character character, Identifier itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier=1, bool spawnItemIfNotFound=false)
AIObjectiveContainItem(Character character, ImmutableHashSet< Identifier > itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier=1, bool spawnItemIfNotFound=false)
ImmutableHashSet< Identifier > ignoredContainerIdentifiers
override void Act(float deltaTime)
override bool CheckObjectiveSpecific()
Should return whether the objective is completed or not.
readonly ImmutableHashSet< Identifier > itemIdentifiers
bool RemoveExistingWhenNecessary
Only remove existing items when the contain target can't be put in the inventory
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
Submarine Submarine
Definition: Entity.cs:53
virtual bool CanBePutInSlot(Item item, int i, bool ignoreCondition=false)
Can the item be put in the specified slot.
virtual 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
bool Contains(Item item)
Is the item contained in this inventory. Does not recursively check items inside items.
List< int > FindIndices(Item item)
Find the indices of all the slots the item is contained in (two-hand items for example can be in mult...
Item FindItem(Func< Item, bool > predicate, bool recursive)
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
int FindIndex(Item item)
Find the index of the first slot the item is contained in.
IEnumerable< Item > GetItemsAt(int index)
Get all the item stored in the specified inventory slot. Can return more than one item if the slot co...
bool CanBePut(Item item)
Can the item be put in the inventory (i.e. is there a suitable free slot or a stack the item can be p...
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
bool HasAccess(Character character)
Used by the AI to check whether they can (in principle) and are allowed (in practice) to interact wit...
bool ShouldBeContained(string[] identifiersOrTags, out bool isRestrictionsDefined)