Files
ichni_Official/Assets/Scripts/NewStorySystem/Dialogue/StoryDialogueController.cs
2026-07-05 16:08:23 -04:00

164 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Generic;
using System.Reflection;
using Ichni.Story.UI;
using UnityEngine;
using UnityEngine.Events;
using Yarn.Unity;
using Yarn.Unity.UnityLocalization;
namespace Ichni.Story.Dialogue
{
/// <summary>
/// 故事树 block 与 Yarn <see cref="DialogueRunner"/> 之间的桥接控制器。
/// <para>点击文本块 → 启动对应 Yarn 节点;对话结束 → 持久化剧情变量、标记 block 完成、
/// 重算解锁并存档、依次执行结束回调(如歌曲解锁提示,阶段 4 使用)。</para>
/// </summary>
public class StoryDialogueController : MonoBehaviour
{
public static StoryDialogueController instance;
[Header("References")]
[Tooltip("驱动 Yarn 对话的 DialogueRunner。")]
public DialogueRunner dialogueRunner;
[Tooltip("故事树页面:对话进行时淡出、结束后淡入(可选)。")]
public StoryUIPage storyUIPage;
/// <summary>
/// 对话结束后依次执行并清空的回调队列。供 <c>unlock_song</c> 等 Yarn 命令(阶段 4
/// 把"解锁提示"排队到对话结束后弹出。
/// </summary>
[System.NonSerialized] public List<UnityAction> dialogueEndActions = new List<UnityAction>();
// 当前正在播放对话的 block id对话结束时据此标记完成
private string _activeBlockId;
private void Awake()
{
instance = this;
EnsureUnityLocalisedLineProvider();
}
private void OnEnable()
{
if (dialogueRunner != null && dialogueRunner.onDialogueComplete != null)
dialogueRunner.onDialogueComplete.AddListener(HandleDialogueComplete);
}
private void OnDisable()
{
if (dialogueRunner != null && dialogueRunner.onDialogueComplete != null)
dialogueRunner.onDialogueComplete.RemoveListener(HandleDialogueComplete);
}
/// <summary>
/// 播放某文本块对应的 Yarn 节点。若对话已在进行、引用缺失或节点名为空则忽略。
/// </summary>
public void PlayBlock(TextBlockView block)
{
if (block == null) return;
if (dialogueRunner == null)
{
Debug.LogError("[StoryDialogueController] 未配置 DialogueRunner无法开始对话。");
return;
}
if (dialogueRunner.IsDialogueRunning)
{
Debug.LogWarning("[StoryDialogueController] 已有对话在进行中,忽略本次点击。");
return;
}
if (string.IsNullOrEmpty(block.YarnNodeName))
{
Debug.LogWarning($"[StoryDialogueController] block '{block.blockId}' 未配置 yarnNodeName无法开始对话。");
return;
}
// 将 DialogueRunner 的 YarnProject 对齐到当前章节(支持多章节共用一个 Runner
EnsureProjectForCurrentChapter();
_activeBlockId = block.blockId;
if (storyUIPage != null)
storyUIPage.FadeOut();
// StartDialogue 返回 YarnTask此处即发即忘完成由 onDialogueComplete 处理
dialogueRunner.StartDialogue(block.YarnNodeName).Forget();
}
// 将 DialogueRunner 的 YarnProject 切换为当前已构建章节的 StoryData.yarnProject若不同且未在运行
private void EnsureProjectForCurrentChapter()
{
StoryTreeController tree = StoryManager.instance != null ? StoryManager.instance.treeController : null;
StoryData data = tree != null ? tree.ActiveStoryData : null;
if (data == null || data.yarnProject == null) return;
if (dialogueRunner.YarnProject != data.yarnProject && !dialogueRunner.IsDialogueRunning)
dialogueRunner.SetProject(data.yarnProject);
}
/// <summary>
/// DialogueRunner.lineProvider 为 [SerializeReference] internal 字段。
/// Inspector 的 "Use Unity Localised Line Provider" 按钮有时无法将 MonoBehaviour
/// 引用正确持久化到该字段;此方法在运行时用反射补救,确保使用正确的 Provider。
/// </summary>
private void EnsureUnityLocalisedLineProvider()
{
if (dialogueRunner == null) return;
FieldInfo field = typeof(DialogueRunner).GetField(
"lineProvider",
BindingFlags.NonPublic | BindingFlags.Instance);
if (field == null) return;
// 已经是 UnityLocalisedLineProvider 则无需处理
if (field.GetValue(dialogueRunner) is UnityLocalisedLineProvider) return;
UnityLocalisedLineProvider provider =
dialogueRunner.GetComponent<UnityLocalisedLineProvider>();
if (provider != null)
{
field.SetValue(dialogueRunner, provider);
Debug.Log("[StoryDialogueController] Assigned UnityLocalisedLineProvider to DialogueRunner.lineProvider.");
}
else
{
Debug.LogWarning("[StoryDialogueController] UnityLocalisedLineProvider not found on DialogueRunner's GameObject. Localized line text will be unavailable.");
}
}
private void HandleDialogueComplete()
{
// 持久化对话过程中写入的全局剧情变量
if (GameSaveManager.instance != null && GameSaveManager.instance.StorySaveModule != null)
GameSaveManager.instance.StorySaveModule.SaveVariables();
// 标记 block 完成 → 重算解锁 → 存档OnBlockCompleted 内部已去重并保存章节进度)
if (!string.IsNullOrEmpty(_activeBlockId) &&
StoryManager.instance != null && StoryManager.instance.treeController != null)
{
StoryManager.instance.treeController.OnBlockCompleted(_activeBlockId);
}
_activeBlockId = null;
// 依次执行并清空结束回调(如歌曲解锁提示)
if (dialogueEndActions.Count > 0)
{
List<UnityAction> pending = new List<UnityAction>(dialogueEndActions);
dialogueEndActions.Clear();
foreach (UnityAction action in pending)
action?.Invoke();
}
if (storyUIPage != null)
storyUIPage.FadeIn();
}
}
}