Files
ichni_Official/Assets/Scripts/NewStorySystem/YarnFunctions/StoryTreeCommands.cs
SoulliesOfficial 810d019619 剧情+对话完善
2026-07-21 15:24:42 -04:00

146 lines
6.7 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 Ichni.Story.Dialogue;
using Ichni.Story.UI;
using UnityEngine;
using UnityEngine.Localization.Settings;
using Yarn.Unity;
namespace Ichni.Story.YarnFunctions
{
/// <summary>
/// 包含与故事树节点控制、全局进程、存档交互相关的 Yarn 自定义命令注册器。
/// </summary>
public static class StoryTreeCommands
{
// ── Yarn Commands ────────────────────────────────────────────────────────
/// <summary>
/// 兼容既有 Yarn 脚本:授予歌曲相关的解锁 Key并在当前对话结束后弹出提示。
/// 格式: &lt;&lt;unlock_song unlock_key&gt;&gt;
/// <para>Key 必须使用小写字母、数字和下划线,例如 <c>story_ch0_prologue_completed</c>。</para>
/// 新内容若不需要歌曲提示,优先使用 <see cref="GrantUnlock"/>。
/// </summary>
/// <param name="songUnlockKey">要授予的稳定解锁 Key而不是歌曲显示名称。</param>
[YarnCommand("unlock_song")]
public static void UnlockSong(string songUnlockKey)
{
ExecuteAfterDialogueCommit(() =>
{
if (!TryGrantUnlockKey(songUnlockKey, out bool wasNewlyGranted) || !wasNewlyGranted)
{
return;
}
if (MessageUIPage.instance != null)
MessageUIPage.instance.ShowUnlockMessage(songUnlockKey);
else
Debug.LogError("[StoryTreeCommands] 弹窗失败:场景中未找到 StoryMessageBoxUIPage 实例!");
});
}
/// <summary>
/// 通用内容解锁命令,可用于章节、歌曲、教程及未来内容。
/// 格式: &lt;&lt;grant_unlock unlock_key&gt;&gt;。
/// 本命令只授予并保存 Key不绑定任何 UI 提示;需要提示时应由 Yarn 额外调用 <c>show_message</c>。
/// </summary>
/// <param name="unlockKey">符合 <see cref="UnlockSaveModule.KeyNamingRule"/> 的稳定解锁 Key。</param>
[YarnCommand("grant_unlock")]
public static void GrantUnlock(string unlockKey)
{
// 全局解锁不属于 ChapterStorySaveTimeline 回滚永远不会撤销已提交的 Key
// 但在当前对话尚未正常结束前,也不能因 Back 而提前写入。
ExecuteAfterDialogueCommit(() => TryGrantUnlockKey(unlockKey, out _));
}
/// <summary>
/// 在对话结束后,弹出一个自定义 MessageBox 提示。
/// 会尝试使用 Unity Localization 进行翻译如果找不到对应条目则回退显示原文本Key
/// 格式: &lt;&lt;show_message titleKey contentKey [tableName]&gt;&gt;
/// </summary>
/// <param name="titleKey">标题文本或本地化键</param>
/// <param name="contentKey">正文文本或本地化键</param>
/// <param name="tableName">Unity Localization 中的 String Table 名称</param>
[YarnCommand("show_message")]
public static void ShowMessage(string titleKey, string contentKey, string tableName = "Message")
{
string translatedTitle = GetTranslatedText(tableName, titleKey);
string translatedContent = GetTranslatedText(tableName, contentKey);
Debug.Log($"[StoryTreeCommands] 准备在对话结束后显示自定义弹窗:标题='{translatedTitle}',内容='{translatedContent}'");
ExecuteAfterDialogueCommit(() =>
{
if (MessageUIPage.instance != null)
MessageUIPage.instance.ShowCustomMessage(translatedTitle, translatedContent);
else
Debug.LogError("[StoryTreeCommands] 无法显示自定义弹窗:未找到 MessageUIPage 实例。");
});
}
/// <summary>
/// 在 TextBlock 事务活跃时延后执行全局副作用;无活动事务时立即执行。
/// 这样 Yarn 命令可以在对话和未来的非对话入口共用,同时保持 Back/失败时不落盘的事务边界。
/// </summary>
private static void ExecuteAfterDialogueCommit(System.Action action)
{
if (action == null)
return;
StoryDialogueController controller = StoryDialogueController.instance;
if (controller != null)
{
controller.ExecuteAfterDialogueCommit(action.Invoke);
return;
}
action.Invoke();
}
/// <summary>
/// 所有 Yarn 解锁命令共用的授予入口。它负责模块存在性检查、重复授予的幂等处理及立即 ES3 保存。
/// </summary>
/// <returns>模块已正常处理该 Key 时返回 trueKey 非法或模块尚未初始化时返回 false。</returns>
private static bool TryGrantUnlockKey(string unlockKey, out bool wasNewlyGranted)
{
wasNewlyGranted = false;
UnlockSaveModule unlockSaveModule = GameSaveManager.instance?.UnlockSaveModule;
if (unlockSaveModule == null)
{
Debug.LogError("[StoryTreeCommands] 无法授予解锁 KeyUnlockSaveModule 尚未初始化!");
return false;
}
if (unlockSaveModule.HasKey(unlockKey))
{
Debug.Log($"[StoryTreeCommands] 解锁 Key 已存在:{unlockKey}");
return true;
}
wasNewlyGranted = unlockSaveModule.GrantKey(unlockKey);
if (wasNewlyGranted)
{
Debug.Log($"[StoryTreeCommands] 已成功写入并保存解锁 Key{unlockKey}");
}
return wasNewlyGranted;
}
// ── 内部辅助 ─────────────────────────────────────────────────────────────
private static string GetTranslatedText(string tableName, string key)
{
if (string.IsNullOrEmpty(key)) return string.Empty;
try
{
// 尝试从 Unity Localization 中获取翻译文本
string translated = LocalizationSettings.StringDatabase.GetLocalizedString(tableName, key);
// 若获取失败或为空,则直接原样返回 key 作为兜底文本
return string.IsNullOrEmpty(translated) ? key : translated;
}
catch
{
// 如果表不存在或其他异常,返回原 key
return key;
}
}
}
}