using UniRx;
using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
namespace SoulliesFramework.General
{
public class CommandQueueManager : SerializedMonoBehaviour
{
public static CommandQueueManager Instance { get; private set; }
// --- 新增:游戏状态变化的广播器 ---
///
/// 一个全局的 Subject,当任何可能影响指令执行条件的状态发生变化时
/// (例如:角色死亡、抽牌、获得 Buff 等),都应该调用 OnNext。
/// 正在“等待”的指令会监听这个“心跳”来重新检查它们的条件。
///
public readonly ISubject OnStateChanged = new Subject();
// 队列的入口现在需要一个能接收 Context 的指令工厂
// 1. 我们使用一个标准的 Queue 来存储待执行的指令。
// 这比 Subject 更能明确地表达“队列”的意图。
private readonly Queue> commandQueue = new Queue>();
// 2. 一个布尔值标志,用于追踪管理器当前是否正在执行一个指令。
private bool isBusy = false;
private void Awake()
{
// ... 单例模式代码 ...
Instance = this;
commandQueue
.Select(entry => entry.Item1.Execute(entry.Item2))
.Concat()
.Subscribe(
_ => { /* OnNext */ },
ex => Debug.LogError($"Command Queue Error: {ex}")
)
.AddTo(this);
}
private void Start()
{
var context = new CommandContext();
// 2. 创建第一个指令组(串行执行)。
// 这个组会先等待2秒并打印信息,然后设置一个名为 "TestMessage" 的变量。
var group1 = new CommandGroup(ExecutionMode.Sequential,
new Cmd_WaitAndLog(2f, "第一个指令组,第一步完成。"),
new Cmd_SetVariable("TestMessage", "Hello, Command Queue!")
);
var group2 = new CommandGroup(ExecutionMode.Parallel,
new Cmd_WaitAndLog(1f, "第二个指令组,第一步完成。"),
new Cmd_WaitAndLog(2f, "第二个指令组,第二步完成。")
);
// 3. 创建第二个指令组。
// 这个组只有一个任务:读取并打印出 "TestMessage" 变量。
var group3 = new CommandGroup(ExecutionMode.Parallel,
new Cmd_WaitAndLog(1f, "第三个指令组,第一步完成。"),
new Cmd_GetAndLogVariable("TestMessage"),
new Cmd_WaitAndLog(1f, "第三个指令组,第二步完成。")
);
}
public void AddCommand(ICommand command, CommandContext context = null)
{
context ??= new CommandContext();
// 将指令和其上下文入队
commandQueue.Enqueue(Tuple.Create(command, context));
// 尝试启动队列处理。
// 如果队列当前不忙,这个调用会立即开始处理我们刚刚添加的指令。
// 如果队列正忙,这个调用什么也不做,因为当前指令完成后会自动处理下一个。
ProcessNextInQueue();
}
///
/// 处理队列中的下一个指令。
/// 这是整个系统的“引擎”。
///
private void ProcessNextInQueue()
{
// 3. 如果当前正在忙,或者队列已空,则直接返回。
// 这确保了同一时间永远只有一个指令在执行。
if (isBusy || commandQueue.Count == 0)
{
return;
}
// 4. 标记为“忙碌”,并从队列中取出下一个指令。
isBusy = true;
var nextEntry = commandQueue.Dequeue();
var commandToExecute = nextEntry.Item1;
var context = nextEntry.Item2;
//Debug.Log($"--- [Queue] 开始执行指令: {commandToExecute.GetType().Name} ---");
// 5. 【核心】在这里,我们才真正调用 Execute 方法。
// 此时,可以保证所有之前的指令都已经彻底完成了。
commandToExecute.Execute(context)
.Subscribe(
_ => { /* OnNext: 忽略 */ },
ex => {
// 错误处理:即使出错,也要解锁队列,避免卡死
Debug.LogError($"[Queue] 指令 {commandToExecute.GetType().Name} 执行出错: {ex}");
isBusy = false;
ProcessNextInQueue(); // 尝试执行下一个
},
() => {
// 6. OnCompleted: 当指令成功完成时执行。
//Debug.Log($"--- [Queue] 指令 {commandToExecute.GetType().Name} 执行完毕 ---");
// 7. 解除“忙碌”状态,并立即尝试处理队列中的下一个指令。
// 这就形成了“一个接一个”的链式反应。
isBusy = false;
ProcessNextInQueue();
}
).AddTo(this); // 确保订阅在对象销毁时被清理
}
public void NotifyStateChanged()
{
OnStateChanged.OnNext(Unit.Default);
}
// ... OnDestroy ...
}
}