(e0d673b0d) Test: Don't calculate the distance to the buttons when seeking path. Instead, check the distance when checking the doors. Allows the bots to access the Remora ballast through the door where the button is a bit farther from the door than usually.

This commit is contained in:
Joonas Rikkonen
2019-05-16 05:42:51 +03:00
parent 828150e0ae
commit de2246fe57
85 changed files with 1955 additions and 1760 deletions
@@ -9,27 +9,29 @@ namespace Barotrauma
class AIObjectiveManager
{
// TODO: expose
public const float OrderPriority = 50.0f;
public const float OrderPriority = 70;
public const float RunPriority = 50;
// Constantly increases the priority of the selected objective, unless overridden
public const float baseDevotion = 2;
public List<AIObjective> Objectives { get; private set; }
public List<AIObjective> Objectives { get; private set; } = new List<AIObjective>();
private Character character;
/// <summary>
/// When set above zero, the character will stand still doing nothing until the timer runs out. Only affects idling.
/// When set above zero, the character will stand still doing nothing until the timer runs out. Does not affect orders.
/// </summary>
public float WaitTimer;
public AIObjective CurrentOrder { get; private set; }
public AIObjective CurrentObjective { get; private set; }
public bool IsCurrentObjective<T>() where T : AIObjective => CurrentObjective is T;
public AIObjectiveManager(Character character)
{
this.character = character;
Objectives = new List<AIObjective>();
CreateAutonomousObjectives();
}
public void AddObjective(AIObjective objective)
@@ -46,6 +48,30 @@ namespace Barotrauma
}
public Dictionary<AIObjective, CoroutineHandle> DelayedObjectives { get; private set; } = new Dictionary<AIObjective, CoroutineHandle>();
public void CreateAutonomousObjectives()
{
Objectives.Clear();
AddObjective(new AIObjectiveFindSafety(character, this), delay: Rand.Value() / 2);
AddObjective(new AIObjectiveIdle(character, this), delay: Rand.Value() / 2);
int objectiveCount = Objectives.Count;
foreach (var automaticOrder in character.Info.Job.Prefab.AutomaticOrders)
{
var orderPrefab = Order.PrefabList.Find(o => o.AITag == automaticOrder.aiTag);
if (orderPrefab == null) { throw new Exception("Could not find a matching prefab by ai tag: " + automaticOrder.aiTag); }
// TODO: Similar code is used in CrewManager:815-> DRY
var matchingItems = orderPrefab.ItemIdentifiers.Any() ?
Item.ItemList.FindAll(it => orderPrefab.ItemIdentifiers.Contains(it.Prefab.Identifier) || it.HasTag(orderPrefab.ItemIdentifiers)) :
Item.ItemList.FindAll(it => it.Components.Any(ic => ic.GetType() == orderPrefab.ItemComponentType));
matchingItems.RemoveAll(it => it.Submarine != character.Submarine);
var item = matchingItems.GetRandom();
var order = new Order(orderPrefab, item ?? character.CurrentHull as Entity, item?.Components.FirstOrDefault(ic => ic.GetType() == orderPrefab.ItemComponentType));
AddObjective(CreateObjective(order, automaticOrder.option, character, automaticOrder.priorityModifier), delay: Rand.Value() / 2);
objectiveCount++;
}
WaitTimer = Math.Max(WaitTimer, Rand.Range(0.5f, 1f) * objectiveCount);
}
public void AddObjective(AIObjective objective, float delay, Action callback = null)
{
if (DelayedObjectives.TryGetValue(objective, out CoroutineHandle coroutine))
@@ -75,7 +101,7 @@ namespace Barotrauma
{
var previousObjective = CurrentObjective;
var firstObjective = Objectives.FirstOrDefault();
if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority(this) > firstObjective.GetPriority(this))
if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority() > firstObjective.GetPriority())
{
CurrentObjective = CurrentOrder;
}
@@ -86,18 +112,24 @@ namespace Barotrauma
if (previousObjective != CurrentObjective)
{
CurrentObjective?.OnSelected();
GetObjective<AIObjectiveIdle>()?.SetRandom();
}
return CurrentObjective;
}
public float GetCurrentPriority()
{
return CurrentObjective == null ? 0.0f : CurrentObjective.GetPriority(this);
return CurrentObjective == null ? 0.0f : CurrentObjective.GetPriority();
}
public void UpdateObjectives(float deltaTime)
{
CurrentOrder?.Update(this, deltaTime);
CurrentOrder?.Update(deltaTime);
if (WaitTimer > 0)
{
WaitTimer -= deltaTime;
return;
}
for (int i = 0; i < Objectives.Count; i++)
{
var objective = Objectives[i];
@@ -117,7 +149,7 @@ namespace Barotrauma
}
else
{
objective.Update(this, deltaTime);
objective.Update(deltaTime);
}
}
GetCurrentObjective();
@@ -127,15 +159,41 @@ namespace Barotrauma
{
if (Objectives.Any())
{
Objectives.Sort((x, y) => y.GetPriority(this).CompareTo(x.GetPriority(this)));
Objectives.Sort((x, y) => y.GetPriority().CompareTo(x.GetPriority()));
}
CurrentObjective?.SortSubObjectives(this);
CurrentObjective?.SortSubObjectives();
}
public void DoCurrentObjective(float deltaTime)
{
if (WaitTimer > 0.0f) { WaitTimer -= deltaTime; }
CurrentObjective?.TryComplete(deltaTime);
if (WaitTimer <= 0)
{
CurrentObjective?.TryComplete(deltaTime);
}
else
{
if (CurrentOrder != null)
{
CurrentOrder.TryComplete(deltaTime);
}
else
{
if (character.AIController is HumanAIController humanAI && humanAI.SteeringManager != null)
{
if (!character.AnimController.InWater &&
!character.IsClimbing &&
!humanAI.UnsafeHulls.Contains(character.CurrentHull) &&
!AIObjectiveIdle.IsForbidden(character.CurrentHull))
{
humanAI.SteeringManager.Reset();
}
else
{
CurrentObjective?.TryComplete(deltaTime);
}
}
}
}
}
public void SetOrder(AIObjective objective)
@@ -145,13 +203,22 @@ namespace Barotrauma
public void SetOrder(Order order, string option, Character orderGiver)
{
CurrentOrder = null;
if (order == null) return;
CurrentOrder = CreateObjective(order, option, orderGiver);
if (CurrentOrder == null)
{
// Recreate objectives, because some of them may be removed, if impossible to complete (e.g. due to path finding)
CreateAutonomousObjectives();
}
}
public AIObjective CreateObjective(Order order, string option, Character orderGiver, float priorityModifier = 1)
{
if (order == null) { return null; }
AIObjective newObjective;
switch (order.AITag.ToLowerInvariant())
{
case "follow":
CurrentOrder = new AIObjectiveGoTo(orderGiver, character, true)
newObjective = new AIObjectiveGoTo(orderGiver, character, this, repeat: true, priorityModifier: priorityModifier)
{
CloseEnough = 1.5f,
AllowGoingOutside = true,
@@ -160,38 +227,41 @@ namespace Barotrauma
};
break;
case "wait":
CurrentOrder = new AIObjectiveGoTo(character, character, true)
newObjective = new AIObjectiveGoTo(character, character, this, repeat: true, priorityModifier: priorityModifier)
{
AllowGoingOutside = true
};
break;
case "fixleaks":
CurrentOrder = new AIObjectiveFixLeaks(character);
newObjective = new AIObjectiveFixLeaks(character, this, priorityModifier);
break;
case "chargebatteries":
CurrentOrder = new AIObjectiveChargeBatteries(character, option);
newObjective = new AIObjectiveChargeBatteries(character, this, option, priorityModifier);
break;
case "rescue":
CurrentOrder = new AIObjectiveRescueAll(character);
newObjective = new AIObjectiveRescueAll(character, this, priorityModifier);
break;
case "repairsystems":
CurrentOrder = new AIObjectiveRepairItems(character) { RequireAdequateSkills = option != "all" };
newObjective = new AIObjectiveRepairItems(character, this, priorityModifier) { RequireAdequateSkills = option != "all" };
break;
case "pumpwater":
CurrentOrder = new AIObjectivePumpWater(character, option);
newObjective = new AIObjectivePumpWater(character, this, option, priorityModifier: priorityModifier);
break;
case "extinguishfires":
CurrentOrder = new AIObjectiveExtinguishFires(character);
newObjective = new AIObjectiveExtinguishFires(character, this, priorityModifier);
break;
case "fightintruders":
newObjective = new AIObjectiveFightIntruders(character, this, priorityModifier);
break;
case "steer":
var steering = (order?.TargetEntity as Item)?.GetComponent<Steering>();
if (steering != null) steering.PosToMaintain = steering.Item.Submarine?.WorldPosition;
if (order.TargetItemComponent == null) return;
CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController);
if (order.TargetItemComponent == null) { return null; }
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier) { IsLoop = true };
break;
default:
if (order.TargetItemComponent == null) return;
CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController);
if (order.TargetItemComponent == null) { return null; }
newObjective = new AIObjectiveOperateItem(order.TargetItemComponent, character, this, option, requireEquip: false, useController: order.UseController, priorityModifier: priorityModifier) { IsLoop = true };
break;
}
return newObjective;