(18eb9f712) Fix bots stealing items if they happen to target them before somebody equips them.
This commit is contained in:
@@ -493,20 +493,25 @@ namespace Barotrauma
|
||||
item.ContainedItems.Any(i => i.HasTag(containedTag) && i.ConditionPercentage > conditionPercentage)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hull safety for all ai characters in the team.
|
||||
/// </summary>
|
||||
public static void PropagateHullSafety(Character character, Hull hull)
|
||||
public static void DoForEachCrewMember(Character character, Action<HumanAIController> action)
|
||||
{
|
||||
foreach (var c in Character.CharacterList)
|
||||
{
|
||||
if (c.AIController is HumanAIController humanAi && humanAi.IsFriendly(character))
|
||||
{
|
||||
humanAi.RefreshHullSafety(hull);
|
||||
action(humanAi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the hull safety for all ai characters in the team.
|
||||
/// </summary>
|
||||
public static void PropagateHullSafety(Character character, Hull hull)
|
||||
{
|
||||
DoForEachCrewMember(character, (humanAi) => humanAi.RefreshHullSafety(hull));
|
||||
}
|
||||
|
||||
private void RefreshHullSafety(Hull hull)
|
||||
{
|
||||
if (GetHullSafety(hull) > HULL_SAFETY_THRESHOLD)
|
||||
@@ -577,21 +582,26 @@ namespace Barotrauma
|
||||
private static bool AddTargets<T1, T2>(Character caller, T2 target) where T1 : AIObjectiveLoop<T2>
|
||||
{
|
||||
bool targetAdded = false;
|
||||
foreach (var c in Character.CharacterList)
|
||||
DoForEachCrewMember(caller, humanAI =>
|
||||
{
|
||||
if (IsFriendly(caller, c) && c.AIController is HumanAIController humanAI)
|
||||
var objective = humanAI.ObjectiveManager.GetObjective<T1>();
|
||||
if (objective != null)
|
||||
{
|
||||
var objective = humanAI.ObjectiveManager.GetObjective<T1>();
|
||||
if (objective == null) { continue; }
|
||||
if (!targetAdded && objective.AddTarget(target))
|
||||
{
|
||||
targetAdded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return targetAdded;
|
||||
}
|
||||
|
||||
public static void RemoveTargets<T1, T2>(Character caller, T2 target) where T1 : AIObjectiveLoop<T2>
|
||||
{
|
||||
DoForEachCrewMember(caller, humanAI =>
|
||||
humanAI.ObjectiveManager.GetObjective<T1>()?.ReportedTargets.Remove(target));
|
||||
}
|
||||
|
||||
public float GetHullSafety(Hull hull)
|
||||
{
|
||||
if (hull == null) { return 0; }
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Barotrauma
|
||||
#if DEBUG
|
||||
DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it is completed.");
|
||||
#endif
|
||||
subObjective.OnCompleted();
|
||||
subObjectives.Remove(subObjective);
|
||||
}
|
||||
else if (!subObjective.CanBeCompleted)
|
||||
@@ -93,9 +94,9 @@ namespace Barotrauma
|
||||
|
||||
bool wasCompleted = IsCompleted();
|
||||
Act(deltaTime);
|
||||
if (!wasCompleted && IsCompleted())
|
||||
if (IsCompleted())
|
||||
{
|
||||
Completed?.Invoke();
|
||||
OnCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +214,15 @@ namespace Barotrauma
|
||||
//}
|
||||
}
|
||||
|
||||
protected virtual void OnCompleted()
|
||||
{
|
||||
if (Completed != null)
|
||||
{
|
||||
Completed();
|
||||
Completed = null;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Reset() { }
|
||||
|
||||
protected abstract void Act(float deltaTime);
|
||||
|
||||
+3
@@ -65,5 +65,8 @@ namespace Barotrauma
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(PowerContainer battery)
|
||||
=> new AIObjectiveOperateItem(battery, character, objectiveManager, Option, false, priorityModifier: PriorityModifier) { IsLoop = false };
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, PowerContainer target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveChargeBatteries, PowerContainer>(character, target);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.Extensions;
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -20,7 +21,11 @@ namespace Barotrauma
|
||||
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveExtinguishFires;
|
||||
protected override IEnumerable<Hull> GetList() => Hull.hullList;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Hull target) => new AIObjectiveExtinguishFire(character, target, objectiveManager, PriorityModifier);
|
||||
protected override AIObjective ObjectiveConstructor(Hull target)
|
||||
=> new AIObjectiveExtinguishFire(character, target, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Hull target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveExtinguishFires, Hull>(character, target);
|
||||
|
||||
public static bool IsValidTarget(Hull hull, Character character)
|
||||
{
|
||||
|
||||
+6
-2
@@ -21,14 +21,18 @@ namespace Barotrauma
|
||||
|
||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
// TODO: sorting criteria
|
||||
return 100;
|
||||
}
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target)
|
||||
=> new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveFightIntruders, Character>(character, target);
|
||||
|
||||
public static bool IsValidTarget(Character target, Character character)
|
||||
{
|
||||
if (target == null || target.IsDead || target.Removed) { return false; }
|
||||
|
||||
@@ -773,6 +773,32 @@ namespace Barotrauma
|
||||
{
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
|
||||
}
|
||||
if (subObjectives.Any()) { return; }
|
||||
var repairTool = weldingTool.GetComponent<RepairTool>();
|
||||
if (repairTool == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
|
||||
#endif
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition;
|
||||
// TODO: use the collider size/reach?
|
||||
if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150)
|
||||
{
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
bool canOperate = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach * 1.5f;
|
||||
if (canOperate)
|
||||
{
|
||||
TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak));
|
||||
}
|
||||
else
|
||||
{
|
||||
TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f });
|
||||
}
|
||||
}
|
||||
|
||||
private Vector2 GetStandPosition()
|
||||
|
||||
@@ -27,7 +27,11 @@ namespace Barotrauma
|
||||
public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFixLeaks;
|
||||
protected override float TargetEvaluation() => Targets.Max(t => GetLeakSeverity(t));
|
||||
protected override IEnumerable<Gap> GetList() => Gap.GapList;
|
||||
protected override AIObjective ObjectiveConstructor(Gap gap) => new AIObjectiveFixLeak(gap, character, objectiveManager, PriorityModifier);
|
||||
protected override AIObjective ObjectiveConstructor(Gap gap)
|
||||
=> new AIObjectiveFixLeak(gap, character, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Gap target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveFixLeaks, Gap>(character, target);
|
||||
|
||||
public static bool IsValidTarget(Gap gap, Character character)
|
||||
{
|
||||
|
||||
@@ -103,37 +103,44 @@ namespace Barotrauma
|
||||
if (moveToTarget.CurrentHull == character.CurrentHull &&
|
||||
Vector2.DistanceSquared(character.Position, moveToTarget.Position) < MathUtils.Pow(targetItem.InteractDistance, 2))
|
||||
{
|
||||
int targetSlot = -1;
|
||||
if (equip)
|
||||
if (IsTakenBySomeone(targetItem))
|
||||
{
|
||||
var pickable = targetItem.GetComponent<Pickable>();
|
||||
if (pickable == null)
|
||||
abandon = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
int targetSlot = -1;
|
||||
if (equip)
|
||||
{
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
//check if all the slots required by the item are free
|
||||
foreach (InvSlotType slots in pickable.AllowedSlots)
|
||||
{
|
||||
if (slots.HasFlag(InvSlotType.Any)) { continue; }
|
||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||
var pickable = targetItem.GetComponent<Pickable>();
|
||||
if (pickable == null)
|
||||
{
|
||||
//slot not needed by the item, continue
|
||||
if (!slots.HasFlag(character.Inventory.SlotTypes[i])) { continue; }
|
||||
targetSlot = i;
|
||||
//slot free, continue
|
||||
if (character.Inventory.Items[i] == null) { continue; }
|
||||
//try to move the existing item to LimbSlot.Any and continue if successful
|
||||
if (character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any })) { continue; }
|
||||
//if everything else fails, simply drop the existing item
|
||||
character.Inventory.Items[i].Drop(character);
|
||||
abandon = true;
|
||||
return;
|
||||
}
|
||||
//check if all the slots required by the item are free
|
||||
foreach (InvSlotType slots in pickable.AllowedSlots)
|
||||
{
|
||||
if (slots.HasFlag(InvSlotType.Any)) { continue; }
|
||||
for (int i = 0; i < character.Inventory.Items.Length; i++)
|
||||
{
|
||||
//slot not needed by the item, continue
|
||||
if (!slots.HasFlag(character.Inventory.SlotTypes[i])) { continue; }
|
||||
targetSlot = i;
|
||||
//slot free, continue
|
||||
if (character.Inventory.Items[i] == null) { continue; }
|
||||
//try to move the existing item to LimbSlot.Any and continue if successful
|
||||
if (character.Inventory.TryPutItem(character.Inventory.Items[i], character, new List<InvSlotType>() { InvSlotType.Any })) { continue; }
|
||||
//if everything else fails, simply drop the existing item
|
||||
character.Inventory.Items[i].Drop(character);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
targetItem.TryInteract(character, false, true);
|
||||
if (targetSlot > -1 && !character.HasEquippedItem(targetItem))
|
||||
{
|
||||
character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character);
|
||||
targetItem.TryInteract(character, false, true);
|
||||
if (targetSlot > -1 && !character.HasEquippedItem(targetItem))
|
||||
{
|
||||
character.Inventory.TryPutItem(targetItem, targetSlot, false, false, character);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -185,23 +192,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; }
|
||||
}
|
||||
|
||||
// TODO: need to exclude items that already are in the inventory?
|
||||
//// Don't allow to get items in rooms that have a fire (except fire extinguishers) or an enemy inside (except weapons)
|
||||
//if (!itemIdentifiers.Contains("extinguisher") && item.CurrentHull.FireSources.Count > 0 ||
|
||||
// item.GetComponent<MeleeWeapon>() == null && item.GetComponent<RangedWeapon>() == null && Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { continue; }
|
||||
|
||||
//if the item is inside a character's inventory, don't steal it unless the character is dead
|
||||
if (item.ParentInventory is CharacterInventory)
|
||||
{
|
||||
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
||||
}
|
||||
//if the item is inside an item, which is inside a character's inventory, don't steal it
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||
{
|
||||
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { continue; }
|
||||
}
|
||||
if (IsTakenBySomeone(item)) { continue; }
|
||||
float itemPriority = 0.0f;
|
||||
if (GetItemPriority != null)
|
||||
{
|
||||
@@ -209,6 +200,7 @@ namespace Barotrauma
|
||||
itemPriority = GetItemPriority(item);
|
||||
if (itemPriority <= 0.0f) { continue; }
|
||||
}
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
itemPriority -= Vector2.Distance((rootContainer ?? item).Position, character.Position) * 0.01f;
|
||||
//ignore if the item has a lower priority than the currently selected one
|
||||
if (moveToTarget != null && itemPriority < currItemPriority) { continue; }
|
||||
@@ -271,10 +263,29 @@ namespace Barotrauma
|
||||
{
|
||||
bool isEquipped = !equip || character.HasEquippedItem(item);
|
||||
if (character.Inventory.Items.Contains(item) && isEquipped) { return true; }
|
||||
if (!equip)
|
||||
{
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||
{
|
||||
return rootContainer.ParentInventory.Owner == character;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool IsTakenBySomeone(Item item)
|
||||
{
|
||||
//if the item is inside a character's inventory, don't steal it unless the character is dead
|
||||
if (item.ParentInventory is CharacterInventory)
|
||||
{
|
||||
if (item.ParentInventory.Owner is Character owner && !owner.IsDead) { return true; }
|
||||
}
|
||||
//if the item is inside an item, which is inside a character's inventory, don't steal it unless the character is dead
|
||||
Item rootContainer = item.GetRootContainer();
|
||||
if (rootContainer != null && rootContainer.ParentInventory is CharacterInventory)
|
||||
{
|
||||
return rootContainer.ParentInventory.Owner == character;
|
||||
if (rootContainer.ParentInventory.Owner is Character owner && !owner.IsDead) { return true; }
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -73,21 +73,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (objectiveManager.CurrentObjective == this)
|
||||
{
|
||||
if (randomTimer > 0)
|
||||
{
|
||||
randomTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetRandom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool IsCompleted() => false;
|
||||
public override bool CanBeCompleted => true;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -87,7 +88,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// the timer is set between 1 and 10 seconds, depending on the priority
|
||||
// the timer is set between 1 and 10 seconds, depending on the priority modifier and a random +-25%
|
||||
private float SetTargetUpdateTimer() => targetUpdateTimer = 1 / MathHelper.Clamp(PriorityModifier * Rand.Range(0.75f, 1.25f), 0.1f, 1);
|
||||
|
||||
public override void Reset()
|
||||
@@ -158,13 +159,18 @@ namespace Barotrauma
|
||||
if (!Objectives.TryGetValue(target, out AIObjective objective))
|
||||
{
|
||||
objective = ObjectiveConstructor(target);
|
||||
objective.Completed += () => ReportedTargets.Remove(target);
|
||||
objective.Completed += () => OnObjectiveCompleted(objective, target);
|
||||
Objectives.Add(target, objective);
|
||||
AddSubObjective(objective);
|
||||
if (!subObjectives.Contains(objective))
|
||||
{
|
||||
subObjectives.Add(objective);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnObjectiveCompleted(AIObjective objective, T target);
|
||||
|
||||
/// <summary>
|
||||
/// List of all possible items of the specified type. Used for filtering the removed objectives.
|
||||
/// </summary>
|
||||
|
||||
@@ -358,6 +358,10 @@ namespace Barotrauma
|
||||
{
|
||||
isCompleted = true;
|
||||
}
|
||||
if (component.AIOperate(deltaTime, character, this))
|
||||
{
|
||||
isCompleted = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -54,7 +54,6 @@ namespace Barotrauma
|
||||
return pumpList;
|
||||
}
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Pump pump) => new AIObjectiveOperateItem(pump, character, objectiveManager, Option, false) { IsLoop = false };
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
if (Option == "stoppumping")
|
||||
@@ -66,5 +65,11 @@ namespace Barotrauma
|
||||
return Targets.Max(t => MathHelper.Lerp(100, 0, Math.Abs(-t.FlowPercentage / 100)));
|
||||
}
|
||||
}
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Pump pump)
|
||||
=> new AIObjectiveOperateItem(pump, character, objectiveManager, Option, false) { IsLoop = false };
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Pump target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectivePumpWater, Pump>(character, target);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -54,7 +54,12 @@ namespace Barotrauma
|
||||
|
||||
protected override float TargetEvaluation() => Targets.Max(t => 100 - t.ConditionPercentage);
|
||||
protected override IEnumerable<Item> GetList() => Item.ItemList;
|
||||
protected override AIObjective ObjectiveConstructor(Item item) => new AIObjectiveRepairItem(character, item, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Item item)
|
||||
=> new AIObjectiveRepairItem(character, item, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Item target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveRepairItems, Item>(character, target);
|
||||
|
||||
public static bool IsValidTarget(Item item, Character character)
|
||||
{
|
||||
|
||||
@@ -22,12 +22,16 @@ namespace Barotrauma
|
||||
|
||||
protected override IEnumerable<Character> GetList() => Character.CharacterList;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override float TargetEvaluation() => Targets.Max(t => GetVitalityFactor(t)) * 100;
|
||||
|
||||
public static float GetVitalityFactor(Character character) => (character.MaxVitality - character.Vitality) / character.MaxVitality;
|
||||
|
||||
protected override AIObjective ObjectiveConstructor(Character target)
|
||||
=> new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier);
|
||||
|
||||
protected override void OnObjectiveCompleted(AIObjective objective, Character target)
|
||||
=> HumanAIController.RemoveTargets<AIObjectiveRescueAll, Character>(character, target);
|
||||
|
||||
public static bool IsValidTarget(Character target, Character character)
|
||||
{
|
||||
if (target == null || target.IsDead || target.Removed) { return false; }
|
||||
|
||||
Reference in New Issue
Block a user