剧情+对话完善
This commit is contained in:
86
Assets/Scripts/NewStorySystem/Dialogue/StoryChoiceMemory.cs
Normal file
86
Assets/Scripts/NewStorySystem/Dialogue/StoryChoiceMemory.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user