(c45394988) Refactor and fix button interaction and access checks in the indoors steering manager.

This commit is contained in:
Joonas Rikkonen
2019-05-16 05:40:00 +03:00
parent 704e0731c9
commit ddd84a4f9e
82 changed files with 1867 additions and 1608 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;