(f0837188e) Bots now target only reported targets when the objective is not an order.
This commit is contained in:
@@ -254,41 +254,56 @@ namespace Barotrauma
|
||||
Order newOrder = null;
|
||||
if (Character.CurrentHull != null)
|
||||
{
|
||||
if (Character.CurrentHull.FireSources.Count > 0)
|
||||
if (AIObjectiveExtinguishFires.IsValidTarget(Character.CurrentHull))
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportfire");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
AddTargets<AIObjectiveExtinguishFires, Hull>(Character, Character.CurrentHull);
|
||||
}
|
||||
|
||||
if (Character.CurrentHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.ConnectedDoor == null && g.Open > 0.0f))
|
||||
foreach (var gap in Character.CurrentHull.ConnectedGaps)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbreach");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
if (AIObjectiveFixLeaks.IsValidTarget(gap))
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbreach");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
AddTargets<AIObjectiveFixLeaks, Gap>(Character, gap);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (AIObjectiveRepairItems.IsValidTarget(item))
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportbrokendevices");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, item.Repairables?.FirstOrDefault());
|
||||
AddTargets<AIObjectiveRepairItems, Item>(Character, item);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.CurrentHull == Character.CurrentHull && !c.IsDead &&
|
||||
(c.AIController is EnemyAIController || (c.TeamID != Character.TeamID && Character.TeamID != Character.TeamType.FriendlyNPC && c.TeamID != Character.TeamType.FriendlyNPC)))
|
||||
if (AIObjectiveFightIntruders.IsValidTarget(c))
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "reportintruders");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
AddTargets<AIObjectiveFightIntruders, Character>(Character, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Character.CurrentHull != null && (Character.Bleeding > 1.0f || Character.Vitality < Character.MaxVitality * 0.1f))
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
// TODO: the objective needs to inherit AIObjectiveLoop<T>
|
||||
if (Character.Bleeding > 1.0f || Character.Vitality < Character.MaxVitality * 0.1f)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == "requestfirstaid");
|
||||
newOrder = new Order(orderPrefab, Character.CurrentHull, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (newOrder != null)
|
||||
{
|
||||
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime))
|
||||
{
|
||||
Character.Speak(
|
||||
newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
Character.Speak(newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,12 +460,9 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var c in Character.CharacterList)
|
||||
{
|
||||
if (c.TeamID == character.TeamID)
|
||||
if (c.AIController is HumanAIController humanAi && humanAi.IsFriendly(character))
|
||||
{
|
||||
if (c.AIController is HumanAIController humanAi)
|
||||
{
|
||||
humanAi.RefreshHullSafety(hull);
|
||||
}
|
||||
humanAi.RefreshHullSafety(hull);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -467,6 +479,62 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static void RefreshTargets(Character character, Order order, Hull hull)
|
||||
{
|
||||
switch (order.AITag)
|
||||
{
|
||||
case "reportfire":
|
||||
AddTargets<AIObjectiveExtinguishFires, Hull>(character, hull);
|
||||
break;
|
||||
case "reportbreach":
|
||||
foreach (var gap in hull.ConnectedGaps)
|
||||
{
|
||||
if (AIObjectiveFixLeaks.IsValidTarget(gap))
|
||||
{
|
||||
AddTargets<AIObjectiveFixLeaks, Gap>(character, gap);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "reportbrokendevices":
|
||||
foreach (var item in Item.ItemList)
|
||||
{
|
||||
if (AIObjectiveRepairItems.IsValidTarget(item))
|
||||
{
|
||||
AddTargets<AIObjectiveRepairItems, Item>(character, item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "reportintruders":
|
||||
foreach (var enemy in Character.CharacterList)
|
||||
{
|
||||
if (AIObjectiveFightIntruders.IsValidTarget(enemy))
|
||||
{
|
||||
AddTargets<AIObjectiveFightIntruders, Character>(character, enemy);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "requestfirstaid":
|
||||
// TODO
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddTargets<T1, T2>(Character caller, T2 target) where T1 : AIObjectiveLoop<T2>
|
||||
{
|
||||
foreach (var c in Character.CharacterList)
|
||||
{
|
||||
if (IsFriendly(caller, c) && c.AIController is HumanAIController humanAI)
|
||||
{
|
||||
var objective = humanAI.ObjectiveManager.GetObjective<T1>();
|
||||
if (objective != null)
|
||||
{
|
||||
objective.AddTarget(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float GetHullSafety(Hull hull)
|
||||
{
|
||||
if (hull == null) { return 0; }
|
||||
@@ -500,7 +568,8 @@ namespace Barotrauma
|
||||
return MathHelper.Clamp(safety * 100, 0, 100);
|
||||
}
|
||||
|
||||
public bool IsFriendly(Character other) => IsFriendly(Character, other);
|
||||
// TODO: If the aliens are quaranteed to be in another team than the player, we wouldn't need to check the species.
|
||||
public bool IsFriendly(Character other) => other.TeamID == Character.TeamID && other.SpeciesName == Character.SpeciesName;
|
||||
public static bool IsFriendly(Character me, Character other) => other.TeamID == me.TeamID && other.SpeciesName == me.SpeciesName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Barotrauma
|
||||
public IEnumerable<AIObjective> SubObjectives => subObjectives;
|
||||
public AIObjective CurrentSubObjective { get; private set; }
|
||||
|
||||
public event Action Completed;
|
||||
|
||||
protected HumanAIController HumanAIController => character.AIController as HumanAIController;
|
||||
protected IndoorsSteeringManager PathSteering => HumanAIController.PathSteering;
|
||||
protected SteeringManager SteeringManager => HumanAIController.SteeringManager;
|
||||
@@ -94,7 +96,12 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
bool wasCompleted = IsCompleted();
|
||||
Act(deltaTime);
|
||||
if (!wasCompleted && IsCompleted())
|
||||
{
|
||||
Completed?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public void AddSubObjective(AIObjective objective)
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Filter(PowerContainer battery)
|
||||
{
|
||||
if (battery == null) { return false; }
|
||||
var item = battery.Item;
|
||||
if (item.Submarine == null) { return false; }
|
||||
if (item.CurrentHull == null) { return false; }
|
||||
|
||||
+2
-1
@@ -23,11 +23,12 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Filter(Hull target)
|
||||
{
|
||||
if (target == null) { return false; }
|
||||
if (target.FireSources.None()) { return false; }
|
||||
if (target.Submarine == null) { return false; }
|
||||
if (target.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(target, true)) { return false; }
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == target && !HumanAIController.IsFriendly(c))) { return false; }
|
||||
//if (Character.CharacterList.Any(c => c.CurrentHull == target && !HumanAIController.IsFriendly(c))) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,14 +25,12 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Filter(Gap gap)
|
||||
{
|
||||
bool ignore = gap.ConnectedWall == null || gap.ConnectedDoor != null || gap.Open <= 0 || gap.linkedTo.All(l => l == null);
|
||||
if (!ignore)
|
||||
{
|
||||
if (gap.Submarine == null) { ignore = true; }
|
||||
else if (gap.Submarine.TeamID != character.TeamID) { ignore = true; }
|
||||
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(gap, true)) { ignore = true; }
|
||||
}
|
||||
return !ignore;
|
||||
if (gap == null) { return false; }
|
||||
if (gap.ConnectedWall == null || gap.ConnectedDoor != null || gap.Open <= 0 || gap.linkedTo.All(l => l == null)) { return false; }
|
||||
if (gap.Submarine == null) { return false; }
|
||||
if (gap.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(gap, true)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
public static float GetLeakSeverity(Gap leak)
|
||||
|
||||
@@ -73,6 +73,21 @@ 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;
|
||||
|
||||
@@ -94,6 +109,13 @@ namespace Barotrauma
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) ||
|
||||
(PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
|
||||
bool currentTargetIsInvalid = currentTarget == null || IsForbidden(currentTarget) ||
|
||||
(PathSteering.CurrentPath != null && PathSteering.CurrentPath.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull)));
|
||||
|
||||
@@ -7,19 +7,34 @@ namespace Barotrauma
|
||||
{
|
||||
abstract class AIObjectiveLoop<T> : AIObjective
|
||||
{
|
||||
protected List<T> targets = new List<T>();
|
||||
protected HashSet<T> targets = new HashSet<T>();
|
||||
protected Dictionary<T, AIObjective> objectives = new Dictionary<T, AIObjective>();
|
||||
protected HashSet<T> ignoreList = new HashSet<T>();
|
||||
private float ignoreListTimer;
|
||||
private float targetUpdateTimer;
|
||||
|
||||
private static AIObjectiveLoop<T> instance;
|
||||
|
||||
// By default, doesn't clear the list automatically
|
||||
protected virtual float IgnoreListClearInterval => 0;
|
||||
protected virtual float TargetUpdateInterval => 2;
|
||||
|
||||
public HashSet<T> ReportedTargets { get; private set; } = new HashSet<T>();
|
||||
|
||||
public bool AddTarget(T target)
|
||||
{
|
||||
if (Filter(target))
|
||||
{
|
||||
ReportedTargets.Add(target);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public AIObjectiveLoop(Character character, AIObjectiveManager objectiveManager, float priorityModifier, string option = null)
|
||||
: base(character, objectiveManager, priorityModifier, option)
|
||||
{
|
||||
instance = this;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -118,8 +133,14 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (T item in GetList())
|
||||
{
|
||||
if (objectiveManager.CurrentOrder != this)
|
||||
{
|
||||
// Battery or pump states cannot be reported and therefore we must ignore them -> the bots always know if they require attention.
|
||||
bool ignore = this is AIObjectiveChargeBatteries || this is AIObjectivePumpWater;
|
||||
if (!ignore && !ReportedTargets.Contains(item)) { continue; }
|
||||
}
|
||||
if (!Filter(item)) { continue; }
|
||||
if (!ignoreList.Contains(item) && !targets.Contains(item))
|
||||
if (!ignoreList.Contains(item))
|
||||
{
|
||||
targets.Add(item);
|
||||
}
|
||||
@@ -133,6 +154,7 @@ namespace Barotrauma
|
||||
if (!objectives.TryGetValue(target, out AIObjective objective))
|
||||
{
|
||||
objective = ObjectiveConstructor(target);
|
||||
objective.Completed += () => ReportedTargets.Remove(target);
|
||||
objectives.Add(target, objective);
|
||||
AddSubObjective(objective);
|
||||
}
|
||||
@@ -148,5 +170,7 @@ namespace Barotrauma
|
||||
|
||||
protected abstract AIObjective ObjectiveConstructor(T target);
|
||||
protected abstract bool Filter(T target);
|
||||
|
||||
public static bool IsValidTarget(T target) => instance == null ? false : instance.Filter(target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Filter(Pump pump)
|
||||
{
|
||||
if (pump == null) { return false; }
|
||||
if (pump.Item.HasTag("ballast")) { return false; }
|
||||
if (pump.Item.Submarine == null) { return false; }
|
||||
if (pump.Item.CurrentHull == null) { return false; }
|
||||
|
||||
+16
-24
@@ -47,34 +47,26 @@ namespace Barotrauma
|
||||
|
||||
protected override bool Filter(Item item)
|
||||
{
|
||||
bool ignore = item.IsFullCondition;
|
||||
if (!ignore)
|
||||
if (item == null) { return false; }
|
||||
if (item.IsFullCondition) { return false; }
|
||||
if (item.CurrentHull == null) { return false; }
|
||||
if (item.Submarine == null) { return false; }
|
||||
if (item.Submarine.TeamID != character.TeamID) { return false; }
|
||||
if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { return false; }
|
||||
if (item.Repairables.None()) { return false; }
|
||||
if (item.CurrentHull.FireSources.Count > 0 || Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { return false; }
|
||||
foreach (Repairable repairable in item.Repairables)
|
||||
{
|
||||
if (item.Submarine == null) { ignore = true; }
|
||||
else if (item.Submarine.TeamID != character.TeamID) { ignore = true; }
|
||||
else if (character.Submarine != null && !character.Submarine.IsEntityFoundOnThisSub(item, true)) { ignore = true; }
|
||||
else
|
||||
if (!objectives.ContainsKey(item) && item.Condition > repairable.ShowRepairUIThreshold)
|
||||
{
|
||||
if (item.Repairables.None()) { ignore = true; }
|
||||
else if (item.CurrentHull != null || item.CurrentHull.FireSources.Count > 0 || Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { ignore = true; }
|
||||
else
|
||||
{
|
||||
foreach (Repairable repairable in item.Repairables)
|
||||
{
|
||||
if (!objectives.ContainsKey(item) && item.Condition > repairable.ShowRepairUIThreshold)
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character))
|
||||
{
|
||||
ignore = true;
|
||||
}
|
||||
if (ignore) { break; }
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (RequireAdequateSkills && !repairable.HasRequiredSkills(character))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !ignore;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override float TargetEvaluation() => targets.Max(t => 100 - t.ConditionPercentage);
|
||||
|
||||
@@ -734,6 +734,25 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem.Prefab.DeconstructItems.Any())
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outputContainer.Inventory.Items.All(i => i != null))
|
||||
{
|
||||
targetItem.Drop(dropper: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem.Prefab.DeconstructItems.Any())
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
|
||||
@@ -212,6 +212,33 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2? PosToMaintain
|
||||
{
|
||||
get { return posToMaintain; }
|
||||
set { posToMaintain = value; }
|
||||
}
|
||||
|
||||
struct ObstacleDebugInfo
|
||||
{
|
||||
public Vector2 Point1;
|
||||
public Vector2 Point2;
|
||||
|
||||
public Vector2? Intersection;
|
||||
|
||||
public float Dot;
|
||||
|
||||
public Vector2 AvoidStrength;
|
||||
|
||||
public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength)
|
||||
{
|
||||
Point1 = edge.Point1;
|
||||
Point2 = edge.Point2;
|
||||
Intersection = intersection;
|
||||
Dot = dot;
|
||||
AvoidStrength = avoidStrength;
|
||||
}
|
||||
}
|
||||
|
||||
//edge point 1, edge point 2, avoid strength
|
||||
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
|
||||
|
||||
|
||||
@@ -1444,6 +1444,10 @@ namespace Barotrauma
|
||||
{
|
||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||
}
|
||||
if (!broken)
|
||||
{
|
||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||
}
|
||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||
|
||||
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
|
||||
|
||||
@@ -550,6 +550,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private string roomName;
|
||||
[Editable, Serialize("", true, translationTextTag: "RoomName.")]
|
||||
public string RoomName
|
||||
{
|
||||
get { return roomName; }
|
||||
set
|
||||
{
|
||||
if (roomName == value) { return; }
|
||||
roomName = value;
|
||||
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
|
||||
}
|
||||
}
|
||||
|
||||
public override Rectangle Rect
|
||||
{
|
||||
get
|
||||
|
||||
Reference in New Issue
Block a user