Files
ichni_Official/Assets/Scripts/NewStorySystem/YarnFunctions/StoryTreeCommands.cs

146 lines
6.7 KiB
C#
Raw Normal View History

2026-07-07 01:28:27 -04:00
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"/>。
2026-07-07 01:28:27 -04:00
/// </summary>
/// <param name="songUnlockKey">要授予的稳定解锁 Key而不是歌曲显示名称。</param>
2026-07-07 01:28:27 -04:00
[YarnCommand("unlock_song")]
public static void UnlockSong(string songUnlockKey)
{
2026-07-21 15:24:42 -04:00
ExecuteAfterDialogueCommit(() =>
2026-07-07 01:28:27 -04:00
{
2026-07-21 15:24:42 -04:00
if (!TryGrantUnlockKey(songUnlockKey, out bool wasNewlyGranted) || !wasNewlyGranted)
2026-07-07 01:28:27 -04:00
{
2026-07-21 15:24:42 -04:00
return;
}
if (MessageUIPage.instance != null)
MessageUIPage.instance.ShowUnlockMessage(songUnlockKey);
else
Debug.LogError("[StoryTreeCommands] 弹窗失败:场景中未找到 StoryMessageBoxUIPage 实例!");
});
2026-07-07 01:28:27 -04:00
}
/// <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)
{
2026-07-21 15:24:42 -04:00
// 全局解锁不属于 ChapterStorySaveTimeline 回滚永远不会撤销已提交的 Key
// 但在当前对话尚未正常结束前,也不能因 Back 而提前写入。
ExecuteAfterDialogueCommit(() => TryGrantUnlockKey(unlockKey, out _));
}
2026-07-07 01:28:27 -04:00
/// <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}'");
2026-07-21 15:24:42 -04:00
ExecuteAfterDialogueCommit(() =>
2026-07-07 01:28:27 -04:00
{
2026-07-21 15:24:42 -04:00
if (MessageUIPage.instance != null)
MessageUIPage.instance.ShowCustomMessage(translatedTitle, translatedContent);
else
Debug.LogError("[StoryTreeCommands] 无法显示自定义弹窗:未找到 MessageUIPage 实例。");
2026-07-07 01:28:27 -04:00
});
}
2026-07-21 15:24:42 -04:00
/// <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;
}
2026-07-07 01:28:27 -04:00
// ── 内部辅助 ─────────────────────────────────────────────────────────────
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;
}
}
}
}