3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
18 private Item Container {
get; }
20 private ImmutableArray<Identifier> TargetContainerTags {
get; }
21 private ImmutableHashSet<Identifier> ValidContainableItemIdentifiers {
get; }
22 private static Dictionary<ItemPrefab, ImmutableHashSet<Identifier>> AllValidContainableItemIdentifiers {
get; } =
new Dictionary<ItemPrefab, ImmutableHashSet<Identifier>>();
24 private int itemIndex;
25 private AIObjectiveDecontainItem decontainObjective;
26 private readonly HashSet<Item> ignoredItems =
new HashSet<Item>();
27 private Item targetItem;
28 private readonly
string abandonGetItemDialogueIdentifier =
"dialogcannotfindloadable";
33 Container = container;
40 TargetContainerTags = targetTags;
41 TargetItemCondition = targetCondition;
44 string optionSpecificDialogueIdentifier = $
"{abandonGetItemDialogueIdentifier}.{option}";
45 if (TextManager.ContainsTag(optionSpecificDialogueIdentifier))
47 abandonGetItemDialogueIdentifier = optionSpecificDialogueIdentifier;
50 ValidContainableItemIdentifiers = GetValidContainableItemIdentifiers();
51 if (ValidContainableItemIdentifiers.None())
54 DebugConsole.LogError($
"No valid containable item identifiers found for the Load Item objective targeting {Container}");
61 private enum CheckStatus { Unfinished,
Finished }
63 private ImmutableHashSet<Identifier> GetValidContainableItemIdentifiers()
65 if (AllValidContainableItemIdentifiers.TryGetValue(Container.
Prefab, out var existingIdentifiers))
67 return existingIdentifiers;
71 bool useDefaultContainableItemIdentifiers =
true;
72 var potentialContainablePrefabs = MapEntityPrefab.List
75 var validContainableItemIdentifiers =
new HashSet<Identifier>();
76 foreach (var component
in Container.
Components)
78 if (CheckComponent() == CheckStatus.Finished)
82 CheckStatus CheckComponent()
84 if (component.statusEffectLists !=
null)
86 foreach (var (_, statusEffects) in component.statusEffectLists)
88 if (CheckStatusEffects(statusEffects) == CheckStatus.Finished)
90 return CheckStatus.Finished;
96 foreach (var item
in itemContainer.ContainableItems)
98 if (CheckStatusEffects(item.StatusEffects) == CheckStatus.Finished)
100 return CheckStatus.Finished;
104 return CheckStatus.Unfinished;
105 CheckStatus CheckStatusEffects(IEnumerable<StatusEffect> statusEffects)
107 if (statusEffects ==
null) {
return CheckStatus.Unfinished; }
108 foreach (var statusEffect
in statusEffects)
110 if ((statusEffect.TargetIdentifiers ==
null || statusEffect.TargetIdentifiers.None()) && !statusEffect.HasConditions) {
continue; }
111 switch (TargetItemCondition)
113 case AIObjectiveLoadItems.ItemCondition.Empty:
114 if (!statusEffect.ReducesItemCondition()) {
continue; }
116 case AIObjectiveLoadItems.ItemCondition.Full:
117 if (!statusEffect.IncreasesItemCondition()) {
continue; }
122 useDefaultContainableItemIdentifiers =
false;
123 if (statusEffect.TargetIdentifiers !=
null)
125 foreach (
Identifier target
in statusEffect.TargetIdentifiers)
127 foreach (var prefab
in potentialContainablePrefabs)
129 if (CheckPrefab(prefab, () => prefab.Tags.Contains(target)) == CheckStatus.Finished) {
return CheckStatus.Finished; }
133 foreach (var prefab
in potentialContainablePrefabs)
135 if (CheckPrefab(prefab, () => statusEffect.MatchesTagConditionals(prefab)) == CheckStatus.Finished) {
return CheckStatus.Finished; }
137 CheckStatus CheckPrefab(ItemPrefab prefab, Func<bool> isValid)
139 if (validContainableItemIdentifiers.Contains(prefab.Identifier)) {
return CheckStatus.Unfinished; }
140 if (!isValid()) {
return CheckStatus.Unfinished; }
141 validContainableItemIdentifiers.Add(prefab.Identifier);
142 if (potentialContainablePrefabs.Any(p => !validContainableItemIdentifiers.Contains(p.Identifier))) {
return CheckStatus.Unfinished; }
143 return CheckStatus.Finished;
146 return CheckStatus.Unfinished;
150 var identifiers = useDefaultContainableItemIdentifiers ?
151 potentialContainablePrefabs.Select(p => p.Identifier).ToImmutableHashSet() :
152 validContainableItemIdentifiers.ToImmutableHashSet();
153 AllValidContainableItemIdentifiers.Add(Container.
Prefab, identifiers);
169 else if (targetItem ==
null)
180 if (targetItem.CurrentHull != Container.
CurrentHull)
182 AddDistance(targetItem.WorldPosition, Container.
WorldPosition);
184 void AddDistance(Vector2 startPos, Vector2 targetPos)
186 float yDist = Math.Abs(startPos.Y - targetPos.Y);
188 if (yDist > 100) { dist += yDist * 5; }
192 float distanceFactor =
193 GetDistanceFactor(targetItem.WorldPosition, verticalDistanceMultiplier: 5, maxDistance: 5000, factorAtMinDistance: 0.9f, factorAtMaxDistance: 0);
199 if (decontainObjective !=
null && targetItem.Container != Container)
201 if (!IsValidContainable(targetItem))
204 decontainObjective.Abandon =
true;
209 decontainObjective.Abandon =
true;
221 protected override void Act(
float deltaTime)
223 if (targetItem ==
null)
225 if (
character.
FindItem(ref itemIndex, out
Item item, identifiers: ValidContainableItemIdentifiers, ignoreBroken:
false, customPredicate: IsValidContainable, customPriorityFunction:
GetPriority))
240 float conditionBasedPriority = TargetItemCondition
switch
244 _ =>
throw new NotImplementedException()
249 catch (NotImplementedException)
252 DebugConsole.LogError($
"Unexpected target condition \"{TargetItemCondition}\" in local function GetConditionBasedProperty");
260 if(decontainObjective ==
null && !IsValidContainable(targetItem))
266 TryAddSubObjective(ref decontainObjective,
269 AbandonGetItemDialogueCondition = () => IsValidContainable(targetItem),
270 AbandonGetItemDialogueIdentifier = abandonGetItemDialogueIdentifier,
272 RemoveExistingWhenNecessary =
true,
274 RemoveExistingMax = 1
279 RemoveSubObjective(ref decontainObjective);
290 private bool IsValidContainable(
Item item)
292 if (item ==
null) {
return false; }
293 if (item.
Removed) {
return false; }
294 if (!ValidContainableItemIdentifiers.Contains(item.
Prefab.
Identifier)) {
return false; }
295 if (ignoredItems.Contains(item)) {
return false; }
300 while (parentItem !=
null)
302 if (parentItem.HasTag(Tags.DontTakeItems)) {
return false; }
308 if (AIObjectiveLoadItems.ItemMatchesTargetCondition(item, TargetItemCondition)) {
return false; }
309 if (TargetItemCondition == AIObjectiveLoadItems.ItemCondition.Full)
314 if (item.
ParentInventory is ItemInventory itemInventory && item.
IsContainerPreferred(itemInventory.Container, out
bool _, out
bool isSecondary, requireConditionRestriction:
true) && !isSecondary) {
return false; }
317 if (AIObjectiveLoadItems.IsValidTarget(item.
Container,
character, TargetContainerTags)) {
return false; }
327 decontainObjective =
null;
331 private void IgnoreTargetItem()
333 if(targetItem ==
null) {
return; }
334 ignoredItems.Add(targetItem);
float Priority
Final priority value after all calculations.
readonly Character character
virtual float MaxDevotion
bool CanEquip(Item item, bool allowWearing)
readonly AIObjectiveManager objectiveManager
static float GetDistanceFactor(Vector2 selfPos, Vector2 targetWorldPos, float factorAtMaxDistance, float verticalDistanceMultiplier=3, float maxDistance=10000.0f, float factorAtMinDistance=1.0f)
Get a normalized value representing how close the target position is. The value is a rough estimation...
AIObjectiveLoadItem(Item container, ImmutableArray< Identifier > targetTags, AIObjectiveLoadItems.ItemCondition targetCondition, Identifier option, Character character, AIObjectiveManager objectiveManager, float priorityModifier)
override bool CheckObjectiveState()
Should return whether the objective is completed or not.
override bool AllowWhileHandcuffed
override float GetPriority()
override void Act(float deltaTime)
override Identifier Identifier
static bool ItemMatchesTargetCondition(Item item, ItemCondition targetCondition)
override bool IsValidTarget(Item target)
const float LowestOrderPriority
Maximum priority of an order given to the character (rightmost order in the crew list)
bool HasItem(Item item, bool requireEquipped=false, InvSlotType? slotType=null)
bool FindItem(ref int itemIndex, out Item targetItem, IEnumerable< Identifier > identifiers=null, bool ignoreBroken=true, IEnumerable< Item > ignoredItems=null, IEnumerable< Identifier > ignoredContainerIdentifiers=null, Func< Item, bool > customPredicate=null, Func< Item, float > customPriorityFunction=null, float maxItemDistance=10000, ISpatialEntity positionalReference=null)
Finds the closest item seeking by identifiers or tags from the world. Ignores items that are outside ...
virtual Vector2 WorldPosition
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
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...
bool ConditionIncreasedRecently
Return true if the condition of this item increased within the last second.
Inventory ParentInventory
bool Illegitimate
Item shouldn't be in the player's inventory. If the guards find it, they will consider it as a theft.
bool?? SpawnedInCurrentOutpost
bool IsContainerPreferred(ItemContainer container, out bool isPreferencesDefined, out bool isSecondary, bool requireConditionRestriction=false)
float ConditionPercentage
List< ItemComponent > Components
Entity GetRootInventoryOwner()
bool HasAccess(Character character)
Used by the AI to check whether they can (in principle) and are allowed (in practice) to interact wit...
override bool IsFull(bool takeStacksIntoAccount=false)
Is there room to put more items in the inventory. Doesn't take stacking into account by default.
readonly ItemInventory Inventory
ImmutableHashSet< Identifier > ContainableItemIdentifiers
bool CanBeContained(Item item)
bool ContainsItemsWithSameIdentifier(Item item)
List< RelatedItem > ContainableItems
readonly Identifier Identifier