Files
ichni_Official/Assets/Scripts/NewStorySystem/Progress/TutorialFlowController.cs

140 lines
6.3 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 Ichni.Menu;
using Ichni.Menu.UI;
using System.Collections.Generic;
using UnityEngine;
namespace Ichni.Story
{
/// <summary>
/// TutorialBlock 的菜单层流程协调器。
/// 负责验证配置、生成通用 SelectionBox 选项、写入剧情进度,并在“游玩”时发起 GameScene 运行。
/// 不保存“教程成绩”或“教程是否通关”;本阶段规则是玩家确认游玩或跳过即视为已处理。
/// </summary>
public static class TutorialFlowController
{
/// <summary>
/// 由 <see cref="UI.TutorialBlockView"/> 点击时调用。配置无效或 SelectionBox 页面未配置时只报错,不修改剧情进度。
/// </summary>
public static void RequestTutorial(StoryBlockDefinition tutorialBlock)
{
if (!TryResolveTutorialRunData(tutorialBlock, out ChapterSelectionUnit chapter,
out SongItemData song, out DifficultyData difficulty))
{
return;
}
MenuManager menuManager = MenuManager.instance;
SelectionBoxUIPage selectionBoxPage = menuManager?.selectionBoxUIPage;
if (selectionBoxPage == null)
{
Debug.LogWarning("[TutorialFlowController] MenuManager 未配置 SelectionBoxUIPage教程不会被解锁。");
return;
}
// TutorialFlowController 只描述“有哪些选择、各自触发什么效果”;
// 具体按钮由通用 SelectionBox 在运行时生成,后续其它系统可复用同一 UI。
selectionBoxPage.Show(
tutorialBlock.tutorialName,
string.Empty,
new List<SelectionBoxOption>
{
new("游玩教程", () => PlayTutorial(tutorialBlock, chapter, song, difficulty)),
new("跳过教程", () => SkipTutorial(tutorialBlock))
});
}
private static void PlayTutorial(StoryBlockDefinition tutorialBlock, ChapterSelectionUnit chapter,
SongItemData song, DifficultyData difficulty)
{
MenuManager menuManager = MenuManager.instance;
if (menuManager == null || menuManager.isEnteringGame)
{
Debug.LogWarning("[TutorialFlowController] 菜单正在切换,忽略教程游玩请求。");
return;
}
// 先验证能否启动 GameScene再写入“已处理”进度避免无效配置导致错误解锁。
if (!CanBeginTutorialGame(chapter, song, difficulty))
return;
if (!StoryProgress.ResolveTutorial(tutorialBlock))
return;
// 规则:玩家确认“游玩教程”的瞬间即解锁后续剧情,不等待教程结算。
menuManager.TryBeginGame(chapter, song, difficulty, MenuReturnDestination.Story,
menuManager.storyUIPage);
}
private static void SkipTutorial(StoryBlockDefinition tutorialBlock)
{
// 规则:玩家确认“跳过教程”的瞬间即解锁后续剧情,并保留在故事树。
StoryProgress.ResolveTutorial(tutorialBlock);
}
private static bool CanBeginTutorialGame(ChapterSelectionUnit chapter, SongItemData song, DifficultyData difficulty)
{
if (MenuManager.instance == null || InformationTransistor.instance == null ||
!MenuManager.instance.CanBeginGame)
{
Debug.LogWarning("[TutorialFlowController] MenuManager、InformationTransistor 或 GameScene 预加载未就绪,无法进入教程。");
return false;
}
if (chapter == null || song == null || difficulty == null)
{
Debug.LogWarning("[TutorialFlowController] 教程缺少章节、歌曲或难度配置,无法进入教程。");
return false;
}
return true;
}
/// <summary>
/// 解析 TutorialBlock 的 Key、教程曲目与稳定难度 ID。
/// 任何一项缺失都会失败且不写入剧情变量,防止错误配置意外解锁后续内容。
/// </summary>
private static bool TryResolveTutorialRunData(StoryBlockDefinition tutorialBlock,
out ChapterSelectionUnit chapter, out SongItemData song, out DifficultyData difficulty)
{
chapter = ChapterSelectionManager.instance?.currentChapter;
song = null;
difficulty = null;
if (tutorialBlock == null || tutorialBlock.blockType != StoryBlockType.Tutorial ||
string.IsNullOrWhiteSpace(tutorialBlock.tutorialKey) ||
string.IsNullOrWhiteSpace(tutorialBlock.tutorialProgressVariable) ||
tutorialBlock.tutorialDifficultySaveId < 0)
{
Debug.LogWarning($"[TutorialFlowController] TutorialBlock '{tutorialBlock?.blockId}' 的 Tutorial Key、Progress Variable 或 Difficulty Save ID 未配置完整。");
return false;
}
TutorialCollection collection = ChapterSelectionManager.instance?.tutorialCollection;
if (chapter == null || collection == null || !collection.TryGetTutorialSong(tutorialBlock.tutorialKey, out song))
{
Debug.LogWarning($"[TutorialFlowController] TutorialBlock '{tutorialBlock.blockId}' 无法解析章节或教程曲目。");
return false;
}
if (song.difficultyDataList == null)
{
Debug.LogWarning($"[TutorialFlowController] 教程 '{tutorialBlock.tutorialKey}' 没有难度列表。");
return false;
}
foreach (DifficultyData candidate in song.difficultyDataList)
{
if (candidate != null && candidate.isAvailable &&
candidate.saveDifficultyId == tutorialBlock.tutorialDifficultySaveId)
{
difficulty = candidate;
return true;
}
}
Debug.LogWarning($"[TutorialFlowController] 教程 '{tutorialBlock.tutorialKey}' 中不存在可用的 Difficulty Save ID '{tutorialBlock.tutorialDifficultySaveId}'。");
return false;
}
}
}