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; public readonly Item item; public bool IsPriority { get; set; } private readonly List ignoredContainers = new List(); private AIObjectiveDecontainItem decontainObjective; private int itemIndex = 0; public AIObjectiveCleanupItem(Item item, Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { this.item = item; } protected override float GetPriority() { if (!IsAllowed) { Priority = 0; Abandon = true; return Priority; } else { float distanceFactor = 0.9f; if (!IsPriority && item.CurrentHull != character.CurrentHull) { float yDist = Math.Abs(character.WorldPosition.Y - item.WorldPosition.Y); yDist = yDist > 100 ? yDist * 5 : 0; float dist = Math.Abs(character.WorldPosition.X - item.WorldPosition.X) + yDist; distanceFactor = MathHelper.Lerp(0.9f, 0, MathUtils.InverseLerp(0, 5000, dist)); } 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 (decontainObjective == null) { // Halve the priority until there's a decontain objective (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)) { itemIndex = 0; if (suitableContainer != null) { bool equip = item.GetComponent() != 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 decontainObjective, () => new AIObjectiveDecontainItem(character, item, objectiveManager, targetContainer: suitableContainer.GetComponent()) { Equip = equip, TakeWholeStack = true, DropIfFails = true }, onCompleted: () => { if (equip) { HumanAIController.ReequipUnequipped(); } IsCompleted = true; }, onAbandon: () => { if (equip) { HumanAIController.ReequipUnequipped(); } if (decontainObjective != null && decontainObjective.ContainObjective != null && decontainObjective.ContainObjective.CanBeCompleted) { ignoredContainers.Add(suitableContainer); } else { Abandon = true; } }); } else { Abandon = true; } } else { objectiveManager.GetObjective().Wander(deltaTime); } } protected override bool CheckObjectiveSpecific() { if (item.IgnoreByAI(character)) { Abandon = true; return false; } if (item.ParentInventory != null) { if (item.Container != null && !AIObjectiveCleanupItems.IsValidContainer(item.Container, character, allowUnloading: objectiveManager.HasOrder())) { // Target was picked up or moved by someone. Abandon = true; return false; } } return IsCompleted; } public override void Reset() { base.Reset(); ignoredContainers.Clear(); itemIndex = 0; decontainObjective = null; } public void DropTarget() { if (item != null && character.HasItem(item)) { item.Drop(character); } } } }