剧情+对话完善

This commit is contained in:
SoulliesOfficial
2026-07-21 15:24:42 -04:00
parent 8f230831e9
commit 810d019619
161 changed files with 7271 additions and 1893 deletions

View File

@@ -12,8 +12,8 @@ namespace Ichni
/// 全局存档入口。
/// <para><see cref="SongSaveModule"/> 负责可重复游玩的歌曲成绩与谱面记录;</para>
/// <para><see cref="UnlockSaveModule"/> 负责章节、歌曲及未来内容共用的 Offline 解锁 Key</para>
/// <para><see cref="StorySaveModule"/> 负责章节故事树、对话选项 Yarn 变量。</para>
/// 套数据使用不同文件和不同生命周期,修改任一时不要把另一侧的字段或迁移规则混入其中。
/// <para><see cref="StorySaveModule"/> 负责章节故事树、对话选项、章节 Yarn 变量和 Timeline 回滚快照。</para>
/// 套数据使用不同文件和不同生命周期,修改任一数据域时不要把其它数据域的字段或迁移规则混入其中。
/// </summary>
public class GameSaveManager : SerializedMonoBehaviour
{
@@ -46,9 +46,10 @@ namespace Ichni
UnlockSaveModule = new UnlockSaveModule();
UnlockSaveModule.LoadUnlockKeys();
// 剧情变量属于独立存档,不参与歌曲成绩的 Schema 或 Chart Revision 迁移。
// 剧情变量属于章节存档,不参与歌曲成绩的 Schema 或 Chart Revision 迁移。
// 章节真正被打开时StoryTreeController 才加载对应 ChapterStorySave
// 不在启动阶段加载已经废弃的全局 StoryVariables 文件。
StorySaveModule = new StorySaveModule();
StorySaveModule.LoadVariables();
}
}
@@ -87,6 +88,11 @@ namespace Ichni
songStatus.additionalInfo ??= string.Empty;
songStatus.beatmapSavesByDifficultyId ??= new Dictionary<int, BeatmapSave>();
// 旧存档没有 isTried 字段。任何已拥有有效结算的歌曲都必然曾被尝试过,
// 因而可在不丢失既有 Story SongBlock 进度的前提下安全补齐该字段。
if (songStatus.isCompleted)
songStatus.isTried = true;
foreach (DifficultyData difficulty in song.difficultyDataList)
{
if (difficulty == null || difficulty.saveDifficultyId < 0)
@@ -204,6 +210,7 @@ namespace Ichni
}
songStatus.additionalInfo ??= string.Empty;
songStatus.isTried = true;
BeatmapSave beatmapSave = songStatus.GetOrCreateBeatmapSave(saveDifficultyId);
bool recordChanged = beatmapSave.ApplyPlayResult(recorder, currentChartRevision);
@@ -214,6 +221,38 @@ namespace Ichni
return recordChanged || completionChanged;
}
/// <summary>
/// 标记玩家已确认进入指定歌曲。
/// <para>这是 Story SongBlock 的唯一完成依据:选择歌曲并启动 GameScene 即写入,即使玩家随后中途退出,
/// 也能在下次打开剧情树时恢复对应 Block 的完成状态。该状态不影响 Accuracy、FC、AP、Max Combo 或 isCompleted。</para>
/// </summary>
/// <param name="songName">SongItemData.songName对应 SongSaves 的稳定歌曲 Key。</param>
/// <returns>本次是否首次写入 isTried。</returns>
public bool MarkSongTried(string songName)
{
if (string.IsNullOrWhiteSpace(songName))
{
Debug.LogWarning("Cannot mark a song as tried without a song name.");
return false;
}
songStatusSaves ??= new Dictionary<string, SongStatusSave>();
if (!songStatusSaves.TryGetValue(songName, out SongStatusSave songStatus) || songStatus == null)
{
// 正常流程会在启动时为当前内容创建空记录;此分支仅作为运行时防线,
// 防止新增歌曲尚未完成内容对账时丢失一次已确认的游玩入口。
songStatus = new SongStatusSave(false, string.Empty, new Dictionary<int, BeatmapSave>());
songStatusSaves[songName] = songStatus;
}
if (songStatus.isTried)
return false;
songStatus.isTried = true;
SaveSongStatuses();
return true;
}
/// <summary>
/// 读取歌曲记录并执行内容对账和 Chart Revision 检查。
/// 当前为正式发布前的重置策略:任何非 v1 的旧测试存档都会直接被新 v1 结构覆盖,
@@ -290,5 +329,5 @@ namespace Ichni
}
}
// StorySaveModule 已迁移至 NewStorySystem/Save/StorySaveModule.cs按章节存树/选项,全局存变量)
// StorySaveModule 已迁移至 NewStorySystem/Save/StorySaveModule.cs按章节保存 Block、选项、变量和回滚快照
}