using Barotrauma.Items.Components; using System; using System.Collections.Generic; using System.Linq; using Barotrauma.Extensions; namespace Barotrauma { class AIObjectiveManager { // TODO: expose public const float OrderPriority = 50.0f; // Constantly increases the priority of the selected objective, unless overridden public const float baseDevotion = 2; public List Objectives { get; private set; } private Character character; /// /// When set above zero, the character will stand still doing nothing until the timer runs out (assuming they don't a high priority order active) /// public float WaitTimer; public AIObjective CurrentOrder { get; private set; } public AIObjective CurrentObjective { get; private set; } public AIObjectiveManager(Character character) { this.character = character; Objectives = new List(); } public void AddObjective(AIObjective objective) { if (Objectives.Find(o => o.IsDuplicate(objective)) != null) return; Objectives.Add(objective); } public Dictionary DelayedObjectives { get; private set; } = new Dictionary(); public void AddObjective(AIObjective objective, float delay, Action callback = null) { if (DelayedObjectives.TryGetValue(objective, out CoroutineHandle coroutine)) { CoroutineManager.StopCoroutines(coroutine); DelayedObjectives.Remove(objective); } coroutine = CoroutineManager.InvokeAfter(() => { DelayedObjectives.Remove(objective); AddObjective(objective); callback?.Invoke(); }, delay); DelayedObjectives.Add(objective, coroutine); } public T GetObjective() where T : AIObjective { foreach (AIObjective objective in Objectives) { if (objective is T) return (T)objective; } return null; } private AIObjective GetCurrentObjective() { var previousObjective = CurrentObjective; var firstObjective = Objectives.FirstOrDefault(); if (CurrentOrder != null && firstObjective != null && CurrentOrder.GetPriority(this) > firstObjective.GetPriority(this)) { CurrentObjective = CurrentOrder; } else { CurrentObjective = firstObjective; } if (previousObjective != CurrentObjective) { CurrentObjective?.OnSelected(); } return CurrentObjective; } public float GetCurrentPriority() { return CurrentObjective == null ? 0.0f : CurrentObjective.GetPriority(this); } public void UpdateObjectives(float deltaTime) { CurrentOrder?.Update(this, deltaTime); for (int i = 0; i < Objectives.Count; i++) { var objective = Objectives[i]; if (objective.IsCompleted()) { #if DEBUG DebugConsole.NewMessage($"Removing objective {objective.DebugTag}, because it is completed."); #endif Objectives.Remove(objective); } else if (!objective.CanBeCompleted) { #if DEBUG DebugConsole.NewMessage($"Removing objective {objective.DebugTag}, because it cannot be completed."); #endif Objectives.Remove(objective); } else { objective.Update(this, deltaTime); } } GetCurrentObjective(); } public void SortObjectives() { if (Objectives.Any()) { Objectives.Sort((x, y) => y.GetPriority(this).CompareTo(x.GetPriority(this))); } CurrentObjective?.SortSubObjectives(this); } public void DoCurrentObjective(float deltaTime) { if (CurrentObjective == null || (CurrentObjective.GetPriority(this) < OrderPriority && WaitTimer > 0.0f)) { WaitTimer -= deltaTime; character.AIController.SteeringManager.Reset(); return; } CurrentObjective?.TryComplete(deltaTime); } public void SetOrder(AIObjective objective) { CurrentOrder = objective; } public void SetOrder(Order order, string option, Character orderGiver) { CurrentOrder = null; if (order == null) return; switch (order.AITag.ToLowerInvariant()) { case "follow": CurrentOrder = new AIObjectiveGoTo(orderGiver, character, true) { CloseEnough = 1.5f, AllowGoingOutside = true, IgnoreIfTargetDead = true, FollowControlledCharacter = orderGiver == character }; break; case "wait": CurrentOrder = new AIObjectiveGoTo(character, character, true) { AllowGoingOutside = true }; break; case "fixleaks": CurrentOrder = new AIObjectiveFixLeaks(character); break; case "chargebatteries": CurrentOrder = new AIObjectiveChargeBatteries(character, option); break; case "rescue": CurrentOrder = new AIObjectiveRescueAll(character); break; case "repairsystems": CurrentOrder = new AIObjectiveRepairItems(character) { RequireAdequateSkills = option != "all" }; break; case "pumpwater": CurrentOrder = new AIObjectivePumpWater(character, option); break; case "extinguishfires": CurrentOrder = new AIObjectiveExtinguishFires(character); break; case "steer": var steering = (order?.TargetEntity as Item)?.GetComponent(); 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); break; default: if (order.TargetItemComponent == null) return; CurrentOrder = new AIObjectiveOperateItem(order.TargetItemComponent, character, option, false, null, order.UseController); break; } } } }