using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; public class Plan { // ================== VARIABLES ================== public enum PlanStatus { Running, Paused, Failed, Successful, Completed } // ================== VARIABLES ================== private ITask task; private ITask currentTask; private PlanStatus status; private Worldstate state; private Coroutine currentCoroutine; // ================== PROPERTIES ================== /// <summary> /// Pauses or resumes plan execution. /// Setting this to true changes the plan's status to <see cref="PlanStatus.Paused"/>; /// setting it to false resumes execution (<see cref="PlanStatus.Running"/>). /// </summary> /// <remarks> /// - Use this to temporarily halt plan progress (e.g., for user input or higher-priority interruptions). /// - Does not reset subtask progress; the plan will continue from its current state when unpaused. /// </remarks> public bool Pause { get => status == PlanStatus.Paused; set { status = value ? PlanStatus.Paused : PlanStatus.Running; } } /// <summary> /// Plan Task /// </summary> public ITask Task => task; /// <summary> /// Plan Primitive Tasks in Queue /// </summary> public Queue<ITask> SubTasks { get; private set; } = new(); /// <summary> /// Current Primitive Task in Execution /// </summary> public ITask CurrentTask => currentTask; /// <summary> /// Current Plan Status /// </summary> public PlanStatus Status => status; // ================== CONSTRUCTOR ================== /// <summary> /// Constructor /// </summary> /// <param name="task">Primitive Task or CompoundTask</param> /// <param name="planner">Planner object</param> public Plan(ITask task, HTNPlanner planner) { if (task == null && state == null) { Debug.LogError("TASK IS NULL"); status = PlanStatus.Failed; return; } this.task = task; if (task.IsCompound) { CompoundTask compoundTask = task as CompoundTask; if (compoundTask == null) Debug.LogError("The Task is Null"); List<ITask> subTasks = compoundTask.Decompose(planner); if (subTasks.Count > 0) { SubTasks = new Queue<ITask>(subTasks); currentTask = SubTasks.Dequeue(); status = PlanStatus.Running; return; } status = PlanStatus.Failed; return; } else { status = PlanStatus.Running; SubTasks.Enqueue(task); currentTask = SubTasks.Dequeue(); } Debug.LogError($"Setting the task {task.Name}"); } // ================== PLAN EXECUTION ================== /// <summary> /// Executes the Plan /// </summary> /// <param name="planner">Planner object</param> public void ExecutePlan(HTNPlanner planner) { if (status == PlanStatus.Running) { if (IsPlanValid(planner)) { if (!currentTask.IsTaskFinished(planner)) { PrimitiveTask task = currentTask as PrimitiveTask; if (task != null) { if (task.HasCoroutineImplementation()) { // If we're not already running a coroutine for this task if (currentCoroutine == null) { currentCoroutine = planner.StartCoroutine(task.ExecuteRoutine(planner)); } } else { // Regular execution task.Execute(planner); } } } else { // Clean up any running coroutine if (currentCoroutine != null) { planner.StopCoroutine(currentCoroutine); currentCoroutine = null; } if (SubTasks.Count > 0) SwitchTask(SubTasks.Dequeue()); else status = PlanStatus.Completed; } } else { // Clean up any running coroutine if (currentCoroutine != null) { planner.StopCoroutine(currentCoroutine); currentCoroutine = null; } // Do replanning // // // // // } } } // ================== PLAN EXECUTION ================== /// <summary> /// Verifies if the Plan is still valid /// </summary> /// <param name="task">Primitive Task/param> /// <returns> /// True if all task conditions are satisfied in order to keep running; otherwise, false. /// </returns> private bool IsPlanValid(HTNPlanner planner) { PrimitiveTask primitiveTask = currentTask as PrimitiveTask; if (primitiveTask != null) return primitiveTask.IsTaskValid(planner); Debug.Log($"The {task.Name} task is not a primitive task"); return false; } /// <summary> /// Switches task if available /// </summary> /// <param name="task">Primitive Task object/param> private void SwitchTask(ITask task) => currentTask = task; }