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

87 lines
4.0 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;
using System.Linq;
using Yarn.Unity;
namespace Ichni.Story.Dialogue
{
/// <summary>
/// Yarn 选项的章节内记忆服务。
/// <para>玩家第一次完成某组选项后,保存其 <see cref="DialogueOption.TextID"/>;再次进入同一 TextBlock 时,
/// 若该选项仍存在且可用,则不显示选择 UI直接返回该 Option 给 Yarn。</para>
/// <para>记录随 Timeline 回滚快照一起恢复,因此回滚到选项之前会让玩家重新选择。</para>
/// </summary>
public static class StoryChoiceMemory
{
/// <summary>
/// 根据来源 Block 与当前全部 Option Text ID 生成选项组 Key。
/// Text ID 已由 Yarn 编译结果稳定生成;排序使纯显示顺序调整不会把同一组选项误认为新选项组。
/// </summary>
public static string CreateChoiceKey(string sourceBlockId, DialogueOption[] options)
{
if (string.IsNullOrWhiteSpace(sourceBlockId) || options == null || options.Length == 0 ||
options.Any(option => option == null || string.IsNullOrWhiteSpace(option.TextID)))
{
return null;
}
string optionSignature = string.Join("|", options
.Select(option => option.TextID)
.OrderBy(textId => textId, StringComparer.Ordinal));
return sourceBlockId + "::" + optionSignature;
}
/// <summary>
/// 查找已保存的选项。若 Yarn 改稿后旧 Text ID 已不存在或暂时不可用,
/// 会删除陈旧记录并回退为普通选择界面,避免自动选择错误路线。
/// </summary>
public static bool TryGetRememberedOption(string sourceBlockId, DialogueOption[] options,
out DialogueOption rememberedOption)
{
rememberedOption = null;
StoryTreeController treeController = StoryManager.instance?.treeController;
StorySaveModule saveModule = GameSaveManager.instance?.StorySaveModule;
string choiceKey = CreateChoiceKey(sourceBlockId, options);
if (treeController == null || saveModule == null || string.IsNullOrEmpty(treeController.ActiveChapterIndex) ||
string.IsNullOrEmpty(choiceKey))
{
return false;
}
if (!saveModule.TryGetChoice(treeController.ActiveChapterIndex, choiceKey, out StoryChoiceRecord record))
return false;
rememberedOption = options.FirstOrDefault(option => option != null && option.IsAvailable &&
option.TextID == record.selectedOptionTextId);
if (rememberedOption != null)
return true;
saveModule.RemoveChoice(treeController.ActiveChapterIndex, choiceKey);
return false;
}
/// <summary>
/// 保存玩家首次确认的选项。调用时先捕获所属 Block 的 Marker 快照,
/// 保证该选项及其随后写入的路线变量都能被重开流程一起回滚。
/// </summary>
public static void RememberChoice(string sourceBlockId, DialogueOption[] options, DialogueOption selectedOption)
{
StoryTreeController treeController = StoryManager.instance?.treeController;
StorySaveModule saveModule = GameSaveManager.instance?.StorySaveModule;
string choiceKey = CreateChoiceKey(sourceBlockId, options);
if (treeController == null || saveModule == null || selectedOption == null ||
string.IsNullOrEmpty(treeController.ActiveChapterIndex) || string.IsNullOrEmpty(choiceKey))
{
return;
}
StoryProgress.TryCaptureActiveBlockSnapshot();
saveModule.SetChoice(treeController.ActiveChapterIndex, new StoryChoiceRecord
{
sourceBlockId = sourceBlockId,
choiceKey = choiceKey,
selectedOptionTextId = selectedOption.TextID
});
}
}
}