Story流程+本地化
This commit is contained in:
@@ -124,18 +124,77 @@ namespace Ichni.Story
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选曲页确认启动游戏时调用。只有章节和稳定歌曲 ID 都命中剧情入口目标时,
|
||||
/// 才将对应 SongBlock 标记为完成,并要求 GameScene 返回剧情页。
|
||||
/// 从 SongSelection 页进入当前章节的 StoryPage。
|
||||
/// <para>若 <paramref name="song"/> 在该章节 StoryData 中存在唯一的 SongBlock,视口会定位到它;
|
||||
/// 没有对应 SongBlock 或当前未选中歌曲时,视口回到剧情树开头。</para>
|
||||
/// <para>这不是 Story SongBlock 的“返回”操作,因此会清除一次性选曲上下文,且不会写入歌曲、剧情或选项存档。</para>
|
||||
/// </summary>
|
||||
public bool TryOpenStoryForSong(ChapterSelectionUnit chapter, SongItemData song)
|
||||
{
|
||||
if (chapter == null || treeController == null || storyUIPage == null ||
|
||||
MenuManager.instance == null || MenuManager.instance.songSelectionUIPage == null ||
|
||||
ChapterSelectionManager.instance == null)
|
||||
{
|
||||
Debug.LogWarning("[StoryManager] 从选曲页打开剧情失败:章节、页面或 StoryTree 引用未就绪。");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetStoryData(chapter.chapterIndex) == null)
|
||||
{
|
||||
Debug.LogWarning($"[StoryManager] 章节 '{chapter.chapterIndex}' 未登记 StoryData,无法从选曲页打开剧情。");
|
||||
return false;
|
||||
}
|
||||
|
||||
UnlockSaveModule unlockSaveModule = GameSaveManager.instance?.UnlockSaveModule;
|
||||
if (unlockSaveModule == null || !unlockSaveModule.CanAccessChapter(chapter))
|
||||
{
|
||||
Debug.LogWarning($"[StoryManager] 章节 '{chapter.chapterIndex}' 尚不可进入剧情,已取消从选曲页跳转。");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 此入口只负责浏览剧情。若此前由 SongBlock 打开选曲页,主动进入剧情时应放弃旧的
|
||||
// “目标歌曲 -> GameScene 后返回原 Block”上下文,避免下一次普通游玩误命中旧目标。
|
||||
InformationTransistor.instance?.storySongEntryContext?.Clear();
|
||||
|
||||
ChapterSelectionManager.instance.currentChapter = chapter;
|
||||
AudioManager.SetSwitch(chapter.chapterSwitch);
|
||||
OpenChapter(chapter.chapterIndex);
|
||||
|
||||
StoryBlockView targetBlock = FindSongBlockView(song);
|
||||
// 与 SongSelection 的 Back 保持一致:离开选曲页进入剧情时,必须立即停止当前歌曲试听。
|
||||
// StoryPage 不承载试听音频,若遗漏该步骤会让歌曲预览跨页面持续播放。
|
||||
SongSelectionManager.instance?.StopPreviewSong();
|
||||
MenuManager.instance.songSelectionUIPage.FadeOut();
|
||||
// StoryPage 激活后、淡入前先完成定位。UIPageBase 此时保持 alpha = 0,
|
||||
// 因此玩家不会先看到剧情树开头、再跳到目标 SongBlock。
|
||||
storyUIPage.FadeIn(beforeFadeAction: () =>
|
||||
{
|
||||
if (targetBlock != null)
|
||||
treeController.FocusBlock(targetBlock);
|
||||
else
|
||||
treeController.FocusStart();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 选曲页确认启动游戏时调用。
|
||||
/// <para>
|
||||
/// SongBlock 的完成状态统一由 <c>SongSaves.isTried</c> 判断,并已在
|
||||
/// <c>PrepareGameLaunch</c> 中写入;这里仅清理由 StoryPage 发起的临时选曲上下文。
|
||||
/// GameScene 无论从何种歌曲入口启动,都会返回 SongSelection;TutorialBlock 仍通过其独立
|
||||
/// 的 <c>MenuReturnDestination.Story</c> 流程返回剧情。
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public void HandleSongSelectionConfirmed(ChapterSelectionUnit chapter, SongItemData song)
|
||||
{
|
||||
StorySongEntryContext context = InformationTransistor.instance?.storySongEntryContext;
|
||||
if (context == null || !context.IsTargetSong(chapter, song))
|
||||
if (context == null || !context.isActive)
|
||||
return;
|
||||
|
||||
// isTried 已在 PrepareGameLaunch 中落盘;此处不再直接写 StorySave,避免 SongBlock
|
||||
// 同时拥有一次性选曲上下文与歌曲存档两套完成来源。
|
||||
context.returnToStoryAfterGame = true;
|
||||
// 玩家已经确认进入 GameScene,无论选择的是入口歌曲还是改选了其它歌曲,旧的剧情入口
|
||||
// 上下文都不能遗留到下一次选曲。否则它可能在后续流程中错误影响页面返回或焦点恢复。
|
||||
context.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,6 +250,29 @@ namespace Ichni.Story
|
||||
}).AddTo(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在当前已构建的 StoryTree 中查找歌曲对应的 SongBlock 视图。
|
||||
/// StoryData 自检会提示同章节重复 Song ID;运行时仍采用第一个匹配项作为防御性回退,
|
||||
/// 以避免错误配置直接阻断玩家进入剧情页。
|
||||
/// </summary>
|
||||
private StoryBlockView FindSongBlockView(SongItemData song)
|
||||
{
|
||||
if (song == null || string.IsNullOrWhiteSpace(song.songName) || treeController == null)
|
||||
return null;
|
||||
|
||||
foreach (StoryBlockView view in treeController.blocks)
|
||||
{
|
||||
StoryBlockDefinition definition = treeController.GetBlockDefinition(view.blockId);
|
||||
if (definition != null && definition.blockType == StoryBlockType.Song &&
|
||||
definition.songName == song.songName)
|
||||
{
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除全部剧情存档(所有章节故事树、章节变量与由剧情授予的内容解锁 Key)。
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user