using System; using System.Collections.Generic; using AibisDream.Kit; using AibisDream.SaveSystem; using UnityEngine; using UnityEngine.Events; using AibisDream.Framework; using Yarn.Unity; namespace AibisDream { public class DialogController : Singleton { private DialogueRunner _dialogueRunner; public DialogueRunner DialogueRunner => _dialogueRunner; [SerializeField] private LineAdvanceInput lineAdvanceInput; private string _currentNodeName; private string[] _currentNodeTags; private void Start() { _dialogueRunner = GetComponentInChildren(); _dialogueRunner.onDialogueStart ??= new UnityEvent(); _dialogueRunner.onDialogueComplete ??= new UnityEvent(); _dialogueRunner.onNodeStart ??= new UnityEventString(); _dialogueRunner.onNodeComplete ??= new UnityEventString(); _dialogueRunner.onDialogueStart.AddListener(() => { EnumEventSystem.Global.Send(InteractionEventEnum.DialogStart); }); _dialogueRunner.onDialogueComplete.AddListener(() => { EnumEventSystem.Global.Send(InteractionEventEnum.DialogEnd); ClearCurrentNodeContext(); }); _dialogueRunner.onNodeStart.AddListener(OnNodeStart); _dialogueRunner.onNodeComplete.AddListener(OnNodeComplete); } private void OnEnable() { SingleCastEventSystem.Global.Register(DialogEventEnum.StartNode, StartDialogNode); EnumEventSystem.Global.Register(GameLoopEnum.GameStart, ResetAdvanceMode); EnumEventSystem.Global.Register(GameLoopEnum.GameQuit, ResetAdvanceMode); EnumEventSystem.Global.Register(DialogEventEnum.OptionShow, OnOptionShow); EnumEventSystem.Global.Register(DialogEventEnum.OptionSelected, OnOptionSelected); } private void OnDisable() { SingleCastEventSystem.Global.Unregister(DialogEventEnum.StartNode); EnumEventSystem.Global.UnRegister(GameLoopEnum.GameStart, ResetAdvanceMode); EnumEventSystem.Global.UnRegister(GameLoopEnum.GameQuit, ResetAdvanceMode); EnumEventSystem.Global.UnRegister(DialogEventEnum.OptionShow, OnOptionShow); EnumEventSystem.Global.UnRegister(DialogEventEnum.OptionSelected, OnOptionSelected); } private void ResetAdvanceMode() { advanceMode.Value = new AdvanceMode { isAuto = false, isQuick = false }; isInOption = false; lineSyncToken = null; Time.timeScale = 1f; } #region Yarn控制 /// /// 开启对话 /// /// Yarn组 public void StartDialog(YarnProject yarnProject) { _dialogueRunner.SetProject(yarnProject); _ = _dialogueRunner.StartDialogue("Start"); } public void StopDialog() { _ = _dialogueRunner.Stop(); YarnVariableStorage.Instance.Clear(); DialogUIManager.Instance.HideDialog(); } public void LoadDialog(YarnProject yarnProject) { _dialogueRunner.SetProject(yarnProject); _ = _dialogueRunner.StartDialogue("Center"); } public void StartDialogNode(string nodeName) { // 检查对话是否正在运行 if (_dialogueRunner.IsDialogueRunning) { Debug.LogWarning($"对话正在运行中,无法启动新节点: {nodeName}"); return; } if (CheckIfNodeExistInCurrentYarnProject(nodeName)) { _ = _dialogueRunner.StartDialogue(nodeName); } else { Debug.LogWarning($"节点 {nodeName} 不存在!"); } } private bool CheckIfNodeExistInCurrentYarnProject(string nodeName) { var currentProject = _dialogueRunner.YarnProject; if (currentProject == null) { Debug.LogWarning("当前没有可用的 YarnProject,无法检查节点"); return false; } string[] nodeNames = currentProject.NodeNames; if (Array.Exists(nodeNames, item => item == nodeName)) { return true; } else { Debug.Log($"节点 {nodeName} 不存在!"); return false; } } #endregion /// /// 返回当前正在执行的节点名与节点 tags;尚未启动 / 异常时返回 (null, 空数组)。 /// public (string nodeName, IReadOnlyList tags) GetCurrentNodeContext() { if (string.IsNullOrEmpty(_currentNodeName)) { return (null, Array.Empty()); } return (_currentNodeName, _currentNodeTags ?? Array.Empty()); } private void OnNodeStart(string nodeName) { UpdateCurrentNodeContext(nodeName); if (!SavePointEvaluator.CanAutoSave(out _)) { return; } StartCoroutine(SaveRestoreOrchestrator.AutoSaveRoutine(nodeName)); } private void OnNodeComplete(string nodeName) { ClearCurrentNodeContext(); } private void UpdateCurrentNodeContext(string nodeName) { if (_dialogueRunner == null || _dialogueRunner.Dialogue == null) { ClearCurrentNodeContext(); return; } _currentNodeName = nodeName; var tagsHeader = _dialogueRunner.Dialogue.GetHeaderValue(nodeName, "tags"); _currentNodeTags = string.IsNullOrWhiteSpace(tagsHeader) ? Array.Empty() : tagsHeader.Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries); } private void ClearCurrentNodeContext() { _currentNodeName = null; _currentNodeTags = null; } #region 推进方式修改 public BindProperty advanceMode = new(new AdvanceMode()); public void SwitchAutoMode() { var mode = advanceMode.Value; mode.isAuto = !mode.isAuto; advanceMode.Value = mode; } public void SwitchQuickMode() { var mode = advanceMode.Value; mode.isQuick = !mode.isQuick; advanceMode.Value = mode; Time.timeScale = advanceMode.Value.CurrentMode == AdvanceModeEnum.Quick ? 8f : 1f; } #endregion private LineSyncToken _lineSyncToken; public LineSyncToken lineSyncToken { get => _lineSyncToken; set { _lineSyncToken = value; lineAdvanceInput?.RunLine(value); } } public bool isInOption; public LineSyncState GetDialogState() { if (lineSyncToken != null && lineSyncToken.state != LineSyncState.Advanced) { return lineSyncToken.state; } else if (isInOption) { return LineSyncState.InOption; } else { return LineSyncState.Default; } } private void OnOptionShow() { isInOption = true; } private void OnOptionSelected(LineInfo lineInfo) { isInOption = false; } } public struct AdvanceMode { public bool isAuto; public bool isQuick; public AdvanceModeEnum CurrentMode { get { if (isQuick) { return AdvanceModeEnum.Quick; } else if (isAuto) { return AdvanceModeEnum.Auto; } else { return AdvanceModeEnum.Default; } } } } public enum AdvanceModeEnum { Default, Auto, Quick } }