Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs
2024-06-18 16:50:02 +03:00

270 lines
12 KiB
C#

using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
class AIObjectiveContainItem: AIObjective
{
public override Identifier Identifier { get; set; } = "contain item".ToIdentifier();
protected override bool AllowWhileHandcuffed => false;
public Func<Item, float> GetItemPriority;
public ImmutableHashSet<Identifier> ignoredContainerIdentifiers;
public bool checkInventory = true;
//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)
private readonly bool spawnItemIfNotFound;
//can either be a tag or an identifier
public readonly ImmutableHashSet<Identifier> itemIdentifiers;
public readonly ItemContainer container;
private readonly Item item;
public Item ItemToContain { get; private set; }
public int? TargetSlot;
private AIObjectiveGetItem getItemObjective;
private AIObjectiveGoTo goToObjective;
private readonly HashSet<Item> containedItems = new HashSet<Item>();
public bool AllowToFindDivingGear { get; set; } = true;
public bool AllowDangerousPressure { get; set; }
public float ConditionLevel { get; set; } = 1;
public bool Equip { get; set; }
public bool RemoveEmpty { get; set; } = true;
public bool RemoveExisting { get; set; }
/// <summary>
/// Only remove existing items when the contain target can't be put in the inventory
/// </summary>
public bool RemoveExistingWhenNecessary { get; set; }
public Func<Item, bool> RemoveExistingPredicate { get; set; }
public int? RemoveMax { get; set; }
public bool MoveWholeStack { get; set; }
private int _itemCount = 1;
public int ItemCount
{
get { return _itemCount; }
set
{
_itemCount = Math.Max(value, 1);
}
}
public AIObjectiveContainItem(Character character, Item item, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier)
{
this.container = container;
this.item = item;
}
public AIObjectiveContainItem(Character character, Identifier itemIdentifier, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
: this(character, itemIdentifier.ToEnumerable().ToImmutableHashSet(), container, objectiveManager, priorityModifier, spawnItemIfNotFound) { }
public AIObjectiveContainItem(Character character, ImmutableHashSet<Identifier> itemIdentifiers, ItemContainer container, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool spawnItemIfNotFound = false)
: base(character, objectiveManager, priorityModifier)
{
this.itemIdentifiers = itemIdentifiers;
this.spawnItemIfNotFound = spawnItemIfNotFound;
this.container = container;
}
protected override bool CheckObjectiveSpecific()
{
if (IsCompleted) { return true; }
if (container?.Item == null || !container.Item.HasAccess(character))
{
Abandon = true;
return false;
}
if (item != null)
{
return container.Inventory.Contains(item);
}
else
{
return CountItems();
}
}
private bool CountItems()
{
int containedItemCount = 0;
foreach (Item it in container.Inventory.AllItems)
{
if (CheckItem(it) && IsInTargetSlot(it))
{
containedItemCount++;
}
}
return containedItemCount >= ItemCount;
}
private bool CheckItem(Item item)
{
return item.HasIdentifierOrTags(itemIdentifiers) && item.ConditionPercentage >= ConditionLevel && item.HasAccess(character) && container.ShouldBeContained(item, out _);
}
protected override void Act(float deltaTime)
{
if (container?.Item == null)
{
Abandon = true;
return;
}
ItemToContain = item ?? character.Inventory.FindItem(it =>
CheckItem(it) &&
//ignore items already in the container, unless we're trying to place to a specific slot, and the item's not in it
(it.Container != container.Item || (TargetSlot.HasValue && it.Container.OwnInventory.FindIndex(it) != TargetSlot)),
recursive: true);
if (ItemToContain != null)
{
if (!character.CanInteractWith(ItemToContain, checkLinked: false))
{
Abandon = true;
return;
}
if (character.CanInteractWith(container.Item, checkLinked: false))
{
static bool CanBePut(Inventory inventory, int? targetSlot, Item itemToContain)
{
if (targetSlot.HasValue)
{
return inventory.CanBePutInSlot(itemToContain, targetSlot.Value);
}
else
{
return inventory.CanBePut(itemToContain);
}
}
if (RemoveExisting || (RemoveExistingWhenNecessary && !CanBePut(container.Inventory, TargetSlot, ItemToContain)))
{
HumanAIController.UnequipContainedItems(container.Item, predicate: RemoveExistingPredicate, unequipMax: RemoveMax);
}
else if (RemoveEmpty)
{
HumanAIController.UnequipEmptyItems(container.Item);
}
Inventory originalInventory = ItemToContain.ParentInventory;
var slots = originalInventory?.FindIndices(ItemToContain);
bool TryPutItem(Inventory inventory, int? targetSlot, Item itemToContain)
{
if (targetSlot.HasValue)
{
return inventory.TryPutItem(itemToContain, targetSlot.Value, allowSwapping: false, allowCombine: false, user: character);
}
else
{
return inventory.TryPutItem(itemToContain, user: character);
}
}
if (TryPutItem(container.Inventory, TargetSlot, ItemToContain))
{
if (MoveWholeStack && slots != null)
{
foreach (int slot in slots)
{
foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
{
TryPutItem(container.Inventory, TargetSlot, item);
}
}
}
IsCompleted = item != null || CountItems();
}
else
{
if (ItemToContain.ParentInventory == character.Inventory && character.IsInFriendlySub)
{
ItemToContain.Drop(character);
}
Abandon = true;
}
}
else
{
TryAddSubObjective(ref goToObjective, () => new AIObjectiveGoTo(container.Item, character, objectiveManager, getDivingGearIfNeeded: AllowToFindDivingGear)
{
TargetName = container.Item.Name,
AbortCondition = obj =>
container?.Item == null || container.Item.Removed || !container.Item.HasAccess(character) ||
(container.Item.RootContainer?.OwnInventory?.Locked ?? false) ||
ItemToContain == null || ItemToContain.Removed ||
!ItemToContain.IsOwnedBy(character) || container.Item.GetRootInventoryOwner() is Character c && c != character,
SpeakIfFails = !objectiveManager.IsCurrentOrder<AIObjectiveCleanupItems>(),
endNodeFilter = n => Vector2.DistanceSquared(n.Waypoint.WorldPosition, container.Item.WorldPosition) <= MathUtils.Pow2(AIObjectiveGetItem.MaxReach)
},
onAbandon: () => Abandon = true,
onCompleted: () => RemoveSubObjective(ref goToObjective));
}
}
else
{
if (character.Submarine == null)
{
Abandon = true;
}
else
{
// No matching items in the inventory, try to get an item
TryAddSubObjective(ref getItemObjective, () =>
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, equip: Equip, checkInventory: checkInventory, spawnItemIfNotFound: spawnItemIfNotFound)
{
GetItemPriority = GetItemPriority,
ignoredContainerIdentifiers = ignoredContainerIdentifiers,
ignoredItems = containedItems,
AllowToFindDivingGear = AllowToFindDivingGear,
AllowDangerousPressure = AllowDangerousPressure,
TargetCondition = ConditionLevel,
ItemFilter = (Item potentialItem) =>
{
return (RemoveEmpty ? container.CanBeContained(potentialItem) : container.Inventory.CanBePut(potentialItem)) && container.ShouldBeContained(potentialItem, out _);
},
ItemCount = ItemCount,
TakeWholeStack = MoveWholeStack
}, onAbandon: () =>
{
Abandon = true;
}, onCompleted: () =>
{
if (getItemObjective?.TargetItem != null)
{
containedItems.Add(getItemObjective.TargetItem);
}
RemoveSubObjective(ref getItemObjective);
});
}
}
}
public bool IsInTargetSlot(Item item)
{
if (TargetSlot == null) { return true; }
if (container?.Inventory is ItemInventory inventory)
{
return inventory.IsInSlot(item, (int)TargetSlot);
}
return false;
}
public override void Reset()
{
base.Reset();
getItemObjective = null;
goToObjective = null;
containedItems.Clear();
}
}
}