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

125 lines
5.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 UnityEngine;
namespace Ichni.Story
{
/// <summary>
/// 修改章节剧情进度的唯一业务入口。
/// <para>StoryVariables 只管理内存字典;本类负责在写入前创建 Timeline 回滚快照、保存当前章节、
/// 并刷新剧情树状态。Yarn 指令、TutorialBlock 与未来小游戏都应通过这里写入。</para>
/// </summary>
public static class StoryProgress
{
// 对话开始时由 StoryDialogueController 设定。它用于判断本次变量/选项写入属于哪个 Block
// 从而在 Marker 所在列首次被执行前截取正确快照。
private static string _activeMutationBlockId;
/// <summary>
/// 声明接下来的剧情变量或选项写入属于指定 Block。仅在对话运行期间有效。
/// </summary>
public static void BeginBlockMutation(string blockId)
{
_activeMutationBlockId = blockId;
TryCaptureActiveBlockSnapshot();
}
/// <summary>
/// 结束指定 Block 的写入上下文,避免后续非剧情系统错误沿用旧的来源 Block。
/// </summary>
public static void EndBlockMutation(string blockId)
{
if (_activeMutationBlockId == blockId)
_activeMutationBlockId = null;
}
/// <summary>
/// 为当前写入来源捕获 Marker 快照。无章节、无来源 Block 或无对应 Marker 时安全返回 false。
/// </summary>
public static bool TryCaptureActiveBlockSnapshot()
{
StoryTreeController treeController = StoryManager.instance?.treeController;
string blockId = !string.IsNullOrEmpty(_activeMutationBlockId)
? _activeMutationBlockId
: treeController?.currentBlock?.blockId;
return treeController != null && !string.IsNullOrEmpty(blockId) &&
treeController.EnsureRollbackSnapshotsForBlock(blockId);
}
public static bool SetBoolAndRefresh(string variableName, bool value) =>
TryWrite(variableName, () => StoryVariables.SetBool(variableName, value));
public static bool SetIntAndRefresh(string variableName, int value) =>
TryWrite(variableName, () => StoryVariables.SetInt(variableName, value));
public static bool AddIntAndRefresh(string variableName, int delta) =>
SetIntAndRefresh(variableName, StoryVariables.GetInt(variableName) + delta);
public static bool SetFloatAndRefresh(string variableName, float value) =>
TryWrite(variableName, () => StoryVariables.SetFloat(variableName, value));
public static bool AddFloatAndRefresh(string variableName, float delta) =>
SetFloatAndRefresh(variableName, StoryVariables.GetFloat(variableName) + delta);
public static bool SetStringAndRefresh(string variableName, string value) =>
TryWrite(variableName, () => StoryVariables.SetString(variableName, value));
public static bool RemoveVariableAndRefresh(string variableName) =>
TryWrite(variableName, () => StoryVariables.Remove(variableName));
/// <summary>
/// 处理 TutorialBlock 的“游玩”或“跳过”确认。两种选择都会立即写入进度变量并完成 Block
/// 完成后的 TutorialBlock 仍可点击,是因为其 View 自身允许 Completed 状态交互。
/// </summary>
public static bool ResolveTutorial(StoryBlockDefinition tutorialBlock)
{
if (tutorialBlock == null || tutorialBlock.blockType != StoryBlockType.Tutorial ||
string.IsNullOrWhiteSpace(tutorialBlock.blockId) ||
string.IsNullOrWhiteSpace(tutorialBlock.tutorialProgressVariable))
{
Debug.LogWarning("[StoryProgress] TutorialBlock 缺少必要的 Block ID 或 Progress Variable。");
return false;
}
StoryTreeController treeController = StoryManager.instance?.treeController;
if (treeController == null)
{
Debug.LogWarning("[StoryProgress] StoryTreeController 未就绪,拒绝写入教程进度。");
return false;
}
// Tutorial 不经过 Yarn 对话上下文,显式由自己的 Block ID 触发同一套快照逻辑。
treeController.EnsureRollbackSnapshotsForBlock(tutorialBlock.blockId);
if (!SetBoolAndRefresh(tutorialBlock.tutorialProgressVariable, true))
return false;
treeController.OnBlockCompleted(tutorialBlock.blockId);
return true;
}
/// <summary>
/// 统一完成“检查上下文 → 捕获回滚点 → 写变量 → 保存章节 → 刷新树”的事务性流程。
/// </summary>
private static bool TryWrite(string variableName, System.Action write)
{
if (string.IsNullOrWhiteSpace(variableName))
{
Debug.LogWarning("[StoryProgress] 剧情变量名为空,拒绝写入。");
return false;
}
if (GameSaveManager.instance?.StorySaveModule == null || StoryManager.instance?.treeController == null)
{
Debug.LogWarning("[StoryProgress] 剧情章节或 StorySaveModule 未就绪,拒绝写入变量。");
return false;
}
// 快照必须在写入前完成,才能保证 Marker 回滚真正恢复到该列之前。
TryCaptureActiveBlockSnapshot();
write();
GameSaveManager.instance.StorySaveModule.SaveActiveChapter();
StoryManager.instance.treeController.RefreshAllStates();
return true;
}
}
}