using System.Collections; 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) private bool _skipRequested; /// 当前是否有对话正在播放。 public bool IsDialogueActive { get; private set; } // ── 生命周期 ────────────────────────────────────────────────────── private void Awake() { if (ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; } ServiceLocator.Register(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; } // ── 公开 API ────────────────────────────────────────────────────── /// /// 启动对话序列。若已有对话在播放则忽略新请求。 /// 由 InteractableNPC.Interact() 调用。 /// /// 要播放的对话序列 SO。 /// NPC 标识符,对话结束时随 EVT_NpcDialogueCompleted 广播。 public void StartDialogue(DialogueSequenceSO sequence, string npcId = "") { if (IsDialogueActive || sequence == null) return; IsDialogueActive = true; _skipRequested = false; // 切换到 UI Action Map(禁用玩家移动输入) _inputReader.EnableUIInput(); _onDialogueStarted?.Raise(); StartCoroutine(PlaySequence(sequence, npcId)); } // ── 输入回调 ────────────────────────────────────────────────────── private void OnSubmit() => _skipRequested = true; // ── 内部协程 ────────────────────────────────────────────────────── private IEnumerator PlaySequence(DialogueSequenceSO sequence, string npcId) { // 选择条件变体(查询 ConditionalVariant.conditionFlag — 未实现 WorldStateRegistry 查询时直接使用默认序列) var resolved = ResolveVariant(sequence); foreach (var line in resolved.lines) { _skipRequested = false; _dialogueBox.ShowLine(line); // 等待打字完成,期间允许跳过 yield return new WaitUntil(() => !_dialogueBox.IsTyping || _skipRequested); if (_dialogueBox.IsTyping) _dialogueBox.SkipTyping(); // 等待玩家按 Submit 推进下一行 _skipRequested = false; yield return new WaitUntil(() => _skipRequested); } EndDialogue(npcId); } private void EndDialogue(string npcId) { _dialogueBox.Hide(); IsDialogueActive = false; // 恢复 Gameplay Action Map _inputReader.EnableGameplayInput(); _onDialogueEnded?.Raise(); if (!string.IsNullOrEmpty(npcId)) _onNpcDialogueCompleted?.Raise(npcId); } /// /// 根据 ConditionalVariant 选择正确的序列版本。 /// 按顺序检查 variants:第一个满足 WorldStateRegistry 标志的变体胜出; /// 未注入 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 (!string.IsNullOrEmpty(variant.conditionFlag) && variant.sequence != null && _worldState.HasFlag(variant.conditionFlag)) return variant.sequence; } } return sequence; } } }