using UnityEngine; namespace Ichni.Story { /// /// 外部玩法系统修改剧情进度的统一入口。 /// /// StoryVariables 只负责读写内存中的变量字典,不会自动落盘或刷新故事树。 /// 本类将“写变量、保存变量、重算 Block 状态”收口,避免教程、小游戏等系统重复实现 /// 不完整的存档流程。 /// public static class StoryProgress { /// /// 写入布尔剧情变量、保存到 StorySaveModule,并立即重新推导当前故事树。 /// 可供教程以外的玩法系统使用。 /// public static bool SetBoolAndRefresh(string variableName, bool value) { if (!TrySetBoolAndSave(variableName, value)) return false; StoryManager.instance?.treeController?.RefreshAllStates(); return true; } /// /// 处理一次 TutorialBlock 的选择结果。 /// 无论选择“游玩”还是“跳过”,都会将其进度变量设为 true,并将教程节点标记为完成。 /// 后续 Block 的解锁应依赖 , /// 而不是依赖该节点的 completedBlockIds,以便和 Yarn 及其它剧情变量共用同一条件系统。 /// public static bool ResolveTutorial(StoryBlockDefinition tutorialBlock) { if (tutorialBlock == null || tutorialBlock.blockType != StoryBlockType.Tutorial) { Debug.LogWarning("[StoryProgress] 无法处理空对象或非 Tutorial 类型的 Block。"); return false; } if (string.IsNullOrWhiteSpace(tutorialBlock.blockId) || string.IsNullOrWhiteSpace(tutorialBlock.tutorialProgressVariable)) { Debug.LogWarning($"[StoryProgress] TutorialBlock '{tutorialBlock?.blockId}' 缺少 Block ID 或 Progress Variable,拒绝解锁。"); return false; } StoryTreeController treeController = StoryManager.instance?.treeController; if (treeController == null) { Debug.LogWarning("[StoryProgress] StoryTreeController 未就绪,拒绝写入教程进度。"); return false; } if (!TrySetBoolAndSave(tutorialBlock.tutorialProgressVariable, true)) return false; // completedBlockIds 只负责教程节点自身的 Completed 外观与不可重复点击; // 后续节点应在 StoryData 中以 VariableCondition 读取上面的剧情变量。 treeController.OnBlockCompleted(tutorialBlock.blockId); return true; } /// /// 清除一个章节中所有 TutorialBlock 声明的进度变量。 /// 目前供 StoryTreeController 的章节进度调试重置使用,确保变量条件与 completedBlockIds 一起回到初始状态。 /// public static void ClearTutorialProgressForChapter(StoryData storyData) { if (storyData?.blocks == null || GameSaveManager.instance == null || GameSaveManager.instance.StorySaveModule == null) { return; } bool changed = false; foreach (StoryBlockDefinition block in storyData.blocks) { if (block == null || block.blockType != StoryBlockType.Tutorial || string.IsNullOrWhiteSpace(block.tutorialProgressVariable)) { continue; } if (!StoryVariables.HasVariable(block.tutorialProgressVariable)) continue; StoryVariables.Remove(block.tutorialProgressVariable); changed = true; } if (changed) GameSaveManager.instance.StorySaveModule.SaveVariables(); } private static bool TrySetBoolAndSave(string variableName, bool value) { if (string.IsNullOrWhiteSpace(variableName)) { Debug.LogWarning("[StoryProgress] 剧情变量名为空,拒绝写入。"); return false; } if (GameSaveManager.instance == null || GameSaveManager.instance.StorySaveModule == null) { Debug.LogWarning("[StoryProgress] StorySaveModule 未就绪,拒绝写入剧情变量。"); return false; } StoryVariables.SetBool(variableName, value); GameSaveManager.instance.StorySaveModule.SaveVariables(); return true; } } }