using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Barotrauma.Extensions; namespace Barotrauma { abstract class AIObjective { public virtual float Devotion => AIObjectiveManager.baseDevotion; public abstract string DebugTag { get; } public virtual bool ForceRun => false; public virtual bool KeepDivingGearOn => false; protected readonly List subObjectives = new List(); public float Priority { get; set; } protected readonly Character character; protected string option; protected bool abandon; public virtual bool CanBeCompleted => !abandon && subObjectives.All(so => so.CanBeCompleted); public IEnumerable SubObjectives => subObjectives; public AIObjective CurrentSubObjective { get; private set; } protected HumanAIController HumanAIController => character.AIController as HumanAIController; protected IndoorsSteeringManager PathSteering => HumanAIController.PathSteering; protected SteeringManager SteeringManager => HumanAIController.SteeringManager; public string Option { get { return option; } } public AIObjective(Character character, string option) { this.character = character; this.option = option; #if DEBUG IsDuplicate(null); #endif } /// /// makes the character act according to the objective, or according to any subobjectives that /// need to be completed before this one /// public void TryComplete(float deltaTime) { for (int i = 0; i < subObjectives.Count; i++) { var subObjective = subObjectives[i]; if (subObjective.IsCompleted()) { #if DEBUG DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it is completed."); #endif subObjectives.Remove(subObjective); } else if (!subObjective.CanBeCompleted) { #if DEBUG DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it cannot be completed."); #endif subObjectives.Remove(subObjective); } else if (subObjective.ShouldInterruptSubObjective(subObjective)) { #if DEBUG DebugConsole.NewMessage($"Removing subobjective {subObjective.DebugTag} of {DebugTag}, because it is interrupted."); #endif subObjectives.Remove(subObjective); } } if (!subObjectives.Contains(CurrentSubObjective)) { CurrentSubObjective = null; } foreach (AIObjective objective in subObjectives) { objective.TryComplete(deltaTime); return; } Act(deltaTime); } public void AddSubObjective(AIObjective objective) { if (subObjectives.Any(o => o.IsDuplicate(objective))) return; subObjectives.Add(objective); } public void SortSubObjectives(AIObjectiveManager objectiveManager) { if (subObjectives.None()) { return; } subObjectives.Sort((x, y) => y.GetPriority(objectiveManager).CompareTo(x.GetPriority(objectiveManager))); CurrentSubObjective = SubObjectives.First(); CurrentSubObjective.SortSubObjectives(objectiveManager); } public virtual float GetPriority(AIObjectiveManager objectiveManager) => Priority; public virtual void Update(AIObjectiveManager objectiveManager, float deltaTime) { var subObjective = objectiveManager.CurrentObjective?.CurrentSubObjective; if (objectiveManager.CurrentOrder == this) { Priority = AIObjectiveManager.OrderPriority; } else if (objectiveManager.CurrentObjective == this || subObjective == this) { Priority += Devotion * deltaTime; } Priority = MathHelper.Clamp(Priority, 0, 100); subObjectives.ForEach(so => so.Update(objectiveManager, deltaTime)); } /// /// Checks if the subobjectives in the given collection are removed from the subobjectives. And if so, removes it also from the dictionary. /// protected void SyncRemovedObjectives(Dictionary dictionary, IEnumerable collection) where T2 : AIObjective { foreach (T1 key in collection) { if (dictionary.TryGetValue(key, out T2 objective)) { if (!subObjectives.Contains(objective)) { dictionary.Remove(key); } } } } protected virtual bool ShouldInterruptSubObjective(AIObjective subObjective) => false; public virtual void OnSelected() { } protected abstract void Act(float deltaTime); public abstract bool IsCompleted(); public abstract bool IsDuplicate(AIObjective otherObjective); } }