159 lines
6.1 KiB
C#
159 lines
6.1 KiB
C#
using Barotrauma.Extensions;
|
|
using Barotrauma.Items.Components;
|
|
using Microsoft.Xna.Framework;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace Barotrauma
|
|
{
|
|
class AIObjectiveCleanupItem : AIObjective
|
|
{
|
|
public override Identifier Identifier { get; set; } = "cleanup item".ToIdentifier();
|
|
public override bool KeepDivingGearOn => true;
|
|
public override bool AllowAutomaticItemUnequipping => false;
|
|
protected override bool AllowWhileHandcuffed => false;
|
|
|
|
public readonly Item item;
|
|
public bool IsPriority { get; set; }
|
|
|
|
private readonly List<Item> ignoredContainers = new List<Item>();
|
|
private AIObjectiveMoveItem moveItemObjective;
|
|
private int itemIndex = 0;
|
|
|
|
/// <summary>
|
|
/// Allows <see cref="moveItemObjective"/> to be interrupted if this objective gets abandoned (e.g. due to the item no longer being eligible for cleanup)
|
|
/// </summary>
|
|
protected override bool ConcurrentObjectives => true;
|
|
|
|
public AIObjectiveCleanupItem(Item item, Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
|
: base(character, objectiveManager, priorityModifier)
|
|
{
|
|
this.item = item;
|
|
}
|
|
|
|
protected override float GetPriority()
|
|
{
|
|
if (!IsAllowed)
|
|
{
|
|
HandleDisallowed();
|
|
return Priority;
|
|
}
|
|
else
|
|
{
|
|
float distanceFactor = 0.9f;
|
|
if (!IsPriority && item.CurrentHull != character.CurrentHull)
|
|
{
|
|
distanceFactor = GetDistanceFactor(item.WorldPosition, verticalDistanceMultiplier: 5, maxDistance: 5000,
|
|
factorAtMinDistance: 0.9f, factorAtMaxDistance: 0);
|
|
}
|
|
bool isSelected = character.HasItem(item);
|
|
float selectedBonus = isSelected ? 100 - MaxDevotion : 0;
|
|
float devotion = (CumulatedDevotion + selectedBonus) / 100;
|
|
float reduction = IsPriority ? 1 : isSelected ? 2 : 3;
|
|
float max = AIObjectiveManager.LowestOrderPriority - reduction;
|
|
Priority = MathHelper.Lerp(0, max, MathHelper.Clamp(devotion + (distanceFactor * PriorityModifier), 0, 1));
|
|
if (moveItemObjective == null)
|
|
{
|
|
// Halve the priority until there's a moveItemObjective (a valid container was found).
|
|
Priority /= 2;
|
|
}
|
|
}
|
|
return Priority;
|
|
}
|
|
|
|
protected override void Act(float deltaTime)
|
|
{
|
|
if (subObjectives.Any()) { return; }
|
|
if (HumanAIController.FindSuitableContainer(character, item, ignoredContainers, ref itemIndex, out Item suitableContainer))
|
|
{
|
|
if (suitableContainer != null)
|
|
{
|
|
bool equip = item.GetComponent<Holdable>() != null ||
|
|
item.AllowedSlots.Any(s => s != InvSlotType.Any) &&
|
|
item.AllowedSlots.None(s =>
|
|
s == InvSlotType.Card ||
|
|
s == InvSlotType.Head ||
|
|
s == InvSlotType.Headset ||
|
|
s == InvSlotType.InnerClothes ||
|
|
s == InvSlotType.OuterClothes ||
|
|
s == InvSlotType.HealthInterface);
|
|
|
|
TryAddSubObjective(ref moveItemObjective, () => new AIObjectiveMoveItem(character, item, objectiveManager, targetContainer: suitableContainer.GetComponent<ItemContainer>())
|
|
{
|
|
Equip = equip,
|
|
TakeWholeStack = true,
|
|
DropIfFails = true
|
|
},
|
|
onCompleted: () =>
|
|
{
|
|
if (equip)
|
|
{
|
|
HumanAIController.ReequipUnequipped();
|
|
}
|
|
IsCompleted = true;
|
|
},
|
|
onAbandon: () =>
|
|
{
|
|
if (equip)
|
|
{
|
|
HumanAIController.ReequipUnequipped();
|
|
}
|
|
if (moveItemObjective is { ContainObjective.CanBeCompleted: true })
|
|
{
|
|
ignoredContainers.Add(suitableContainer);
|
|
}
|
|
else
|
|
{
|
|
Abandon = true;
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
Abandon = true;
|
|
}
|
|
}
|
|
objectiveManager.GetObjective<AIObjectiveIdle>().Wander(deltaTime);
|
|
}
|
|
|
|
protected override bool CheckObjectiveState()
|
|
{
|
|
if (item.IgnoreByAI(character) || Item.IsMarkedForDeconstruction(item))
|
|
{
|
|
Abandon = true;
|
|
}
|
|
else if (item.ParentInventory != null && item.GetRootInventoryOwner() != character)
|
|
{
|
|
if (!objectiveManager.HasOrder<AIObjectiveCleanupItems>())
|
|
{
|
|
// Don't allow taking items from containers in the idle state.
|
|
Abandon = true;
|
|
}
|
|
else if (item.Container != null && !AIObjectiveCleanupItems.IsValidContainer(item.Container, character))
|
|
{
|
|
// Target was picked up or moved by someone.
|
|
Abandon = true;
|
|
}
|
|
}
|
|
return !Abandon && IsCompleted;
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
base.Reset();
|
|
ignoredContainers.Clear();
|
|
itemIndex = 0;
|
|
moveItemObjective = null;
|
|
}
|
|
|
|
public void DropTarget()
|
|
{
|
|
if (item != null && character.HasItem(item))
|
|
{
|
|
item.Drop(character);
|
|
}
|
|
}
|
|
}
|
|
}
|