using System.Collections;
using System.Collections.Generic;
using BaseGames.Core;
using BaseGames.Core.Events;
using BaseGames.Input;
using BaseGames.World;
using UnityEngine;
namespace BaseGames.Dialogue
{
///
/// 对话管理器(架构 14_NarrativeModule §4)。
/// 驱动 DialogueUI 打字机效果;管理 Action Map 切换;向 QuestManager 广播对话完成事件。
/// 在 Awake 中注册到 ServiceLocator。
///
public class DialogueManager : MonoBehaviour, IDialogueService
{
[SerializeField] private DialogueUI _dialogueBox;
[SerializeField] private InputReaderSO _inputReader;
[SerializeField] private WorldStateRegistry _worldState;
[Header("事件频道")]
[SerializeField] private VoidEventChannelSO _onDialogueStarted;
[SerializeField] private VoidEventChannelSO _onDialogueEnded;
[SerializeField] private StringEventChannelSO _onNpcDialogueCompleted; // → EVT_NpcDialogueCompleted (npcId)
[SerializeField] private VoidEventChannelSO _onLineStarted; // 每行开始打字时广播
[SerializeField] private VoidEventChannelSO _onLineEnded; // 每行玩家确认后广播
private bool _skipRequested;
// ── 复用 Yield 指令,避免协程每行 new WaitUntil 闭包 ───────────────
private sealed class WaitTypingOrSkip : CustomYieldInstruction
{
private readonly DialogueManager _m;
public WaitTypingOrSkip(DialogueManager m) => _m = m;
public override bool keepWaiting => _m._dialogueBox.IsTyping && !_m._skipRequested;
}
private sealed class WaitSkip : CustomYieldInstruction
{
private readonly DialogueManager _m;
public WaitSkip(DialogueManager m) => _m = m;
public override bool keepWaiting => !_m._skipRequested;
}
private WaitTypingOrSkip _waitTypingOrSkip;
private WaitSkip _waitSkip;
///
/// 当 IsDialogueActive 时排队等待的对话请求。
/// 支持脚本触发的连续对话序列(如剧情链、事件链触发的对话),
/// 但容量上限为 8,避免因误触导致无限排队。
///
private readonly Queue<(DialogueSequenceSO seq, string npcId)> _pending = new();
private const int PendingCapacity = 8;
/// 当前是否有对话正在播放。
public bool IsDialogueActive { get; private set; }
// ── 生命周期 ──────────────────────────────────────────────────────
private void Awake()
{
if (ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; }
ServiceLocator.Register(this);
_waitTypingOrSkip = new WaitTypingOrSkip(this);
_waitSkip = new WaitSkip(this);
}
private void OnDestroy()
{
ServiceLocator.Unregister(this);
}
private void OnEnable()
{
if (_inputReader != null) _inputReader.SubmitEvent += OnSubmit;
}
private void OnDisable()
{
if (_inputReader != null) _inputReader.SubmitEvent -= OnSubmit;
_pending.Clear();
// 若对话协程在组件禁用或场景切换时仍在运行,Unity 会强制杀死协程但不调用
// EndDialogue(),导致 Action Map 永久停留在 UI 模式。此处主动恢复。
if (IsDialogueActive)
{
StopAllCoroutines();
_dialogueBox?.Hide();
IsDialogueActive = false;
_inputReader?.EnableGameplayInput();
}
}
// ── 公开 API ──────────────────────────────────────────────────────
///
/// 启动对话序列。
/// 若已有对话在播放,请求会进入等待队列(上限 ),
/// 待当前对话结束后依序自动播放;超出上限的请求被丢弃。
/// 由 InteractableNPC.Interact() 调用。
///
/// 要播放的对话序列 SO。
/// NPC 标识符,对话结束时随 EVT_NpcDialogueCompleted 广播。
public void StartDialogue(DialogueSequenceSO sequence, string npcId = "")
{
if (sequence == null) return;
if (IsDialogueActive)
{
if (_pending.Count < PendingCapacity)
_pending.Enqueue((sequence, npcId));
return;
}
PlayImmediate(sequence, npcId);
}
private void PlayImmediate(DialogueSequenceSO sequence, string npcId)
{
IsDialogueActive = true;
_skipRequested = false;
if (_inputReader != null) _inputReader.EnableUIInput();
_onDialogueStarted?.Raise();
StartCoroutine(PlaySequence(sequence, npcId));
}
// ── 输入回调 ──────────────────────────────────────────────────────
private void OnSubmit() => _skipRequested = true;
// ── 内部协程 ──────────────────────────────────────────────────────
private IEnumerator PlaySequence(DialogueSequenceSO sequence, string npcId)
{
var resolved = ResolveVariant(sequence);
if (resolved.lines == null || resolved.lines.Length == 0)
{
EndDialogue(npcId);
yield break;
}
foreach (var line in resolved.lines)
{
_skipRequested = false;
_dialogueBox.ShowLine(line);
_onLineStarted?.Raise();
// 等待打字完成,期间允许跳过
yield return _waitTypingOrSkip;
if (_dialogueBox.IsTyping) _dialogueBox.SkipTyping();
// 等待玩家按 Submit 推进下一行
_skipRequested = false;
yield return _waitSkip;
_onLineEnded?.Raise();
}
EndDialogue(npcId);
}
private void EndDialogue(string npcId)
{
_dialogueBox.Hide();
IsDialogueActive = false;
if (_inputReader != null) _inputReader.EnableGameplayInput();
_onDialogueEnded?.Raise();
if (!string.IsNullOrEmpty(npcId))
_onNpcDialogueCompleted?.Raise(npcId);
// 自动播放队首等待中的对话(脚本触发的连续序列)
if (_pending.Count > 0)
{
var (seq, id) = _pending.Dequeue();
PlayImmediate(seq, id);
}
}
///
/// 根据 ConditionalVariant 选择正确的序列版本。
/// 按顺序检查 variants:所有 requiredFlags 均满足的第一个变体胜出(AND 关系);
/// 未注入 WorldStateRegistry 或无满足条件的变体时,返回原序列(默认)。
///
private DialogueSequenceSO ResolveVariant(DialogueSequenceSO sequence)
{
if (sequence.variants == null || sequence.variants.Length == 0)
return sequence;
if (_worldState != null)
{
foreach (var variant in sequence.variants)
{
if (variant.sequence == null) continue;
if (variant.requiredFlags == null || variant.requiredFlags.Length == 0)
return variant.sequence; // 无条件变体:直接采用
bool allMet = true;
foreach (var flag in variant.requiredFlags)
{
if (!string.IsNullOrEmpty(flag) && !_worldState.HasFlag(flag))
{
allMet = false;
break;
}
}
if (allMet) return variant.sequence;
}
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
else if (sequence.variants.Length > 0)
{
Debug.LogWarning(
$"[DialogueManager] 序列 '{sequence.sequenceId}' 有 {sequence.variants.Length} 个条件变体," +
"但 WorldStateRegistry 未注入,将使用默认序列。请检查 Inspector 中的 _worldState 字段。",
this);
}
#endif
return sequence;
}
}
}