From 909f7ded3531eb345e8fcc13b5dea05e8c4f909d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 08:00:53 +0000 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E7=90=86=E5=85=A8=E5=9C=96=E6=96=BD?= =?UTF-8?q?=E6=B3=95=E5=81=B5=E6=B8=AC=E6=AD=BB=E8=AE=8A=E6=95=B8=EF=BC=9B?= =?UTF-8?q?team.dat=20=E4=B8=8D=E5=86=8D=E9=87=8D=E5=BB=BA=E5=B7=B2?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=E7=9A=84=E5=9C=B0=E5=9C=96=E6=AA=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - FeatureDetector:移除 SpellEntireMap 偵測中未使用的 expectedBaseRange 與原版射程計算。 - PatchEngine:套用與還原 team.dat 時僅改寫磁碟上仍存在的檔案, 避免玩家已刪除的地圖被備份內容重建回去;規格文件同步更新方法簽章。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MT7bAj4aeL4tUm7Lvs9FVz --- docs/feature-spec-roman-endless.md | 2 +- src/Core/Features/FeatureDetector.cs | 4 +--- src/Core/Services/PatchEngine.cs | 11 +++++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/feature-spec-roman-endless.md b/docs/feature-spec-roman-endless.md index 8eba838..2739f53 100644 --- a/docs/feature-spec-roman-endless.md +++ b/docs/feature-spec-roman-endless.md @@ -138,7 +138,7 @@ internal static Dictionary Build(BackupManager backup, bool maxP ### 3.3 `src/Core/Services/PatchEngine.cs` - 步驟 G(team.dat)改為傳入兩個布林: - `GetPatchedTeamDatBytes(backupManager, options.MaxPopulation, options.RomanEndless)`。 + `GetPatchedTeamDatBytes(gamePath, backupManager, options.MaxPopulation, options.RomanEndless)`。 - log:新增鍵 `SvcLogTeamDatRoman`(見 §3.6),在 romanEndless 為 true 時輸出 「已將 {n} 張無盡地圖的玩家陣營改為羅馬」。既有 `SvcLogTeamDatApplied/Restored` 邏輯保持。 diff --git a/src/Core/Features/FeatureDetector.cs b/src/Core/Features/FeatureDetector.cs index 37c6882..15452a5 100644 --- a/src/Core/Features/FeatureDetector.cs +++ b/src/Core/Features/FeatureDetector.cs @@ -288,11 +288,9 @@ internal PatchProfile Detect(string gamePath, BackupManager backupManager) // 使用塞爾特祭司 FigKelPri00_Priester 偵測法師施法距離 bool spellEntireMap = false; if (unitRows.TryGetValue("FigKelPri00_Priester", out var testSpellCols) && - origUnitRows.TryGetValue("FigKelPri00_Priester", out var testSpellOrigCols)) + origUnitRows.ContainsKey("FigKelPri00_Priester")) { double curRange = BackupManager.GetUnitMaxRange(testSpellCols, "priest"); - double origRange = BackupManager.GetUnitMaxRange(testSpellOrigCols, "priest"); - double expectedBaseRange = isFileBalanced ? 3840.0 : origRange; if (curRange > 0) { if (Math.Abs(curRange - 30000.0) < 100.0) diff --git a/src/Core/Services/PatchEngine.cs b/src/Core/Services/PatchEngine.cs index 2537032..183678d 100644 --- a/src/Core/Services/PatchEngine.cs +++ b/src/Core/Services/PatchEngine.cs @@ -134,7 +134,7 @@ public void ApplyPatches(string gamePath, PatchProfile profile, BackupManager ba patchedFiles[Path.Combine(gamePath, @"SYSTEM\DATA_MP\DEFAULTS\objdef.dau")] = objdefBytes; // G. team.dat - var teamDatPatches = GetPatchedTeamDatBytes(backupManager, options.MaxPopulation, options.RomanEndless); + var teamDatPatches = GetPatchedTeamDatBytes(gamePath, backupManager, options.MaxPopulation, options.RomanEndless); foreach (var kvp in teamDatPatches) { patchedFiles[Path.Combine(gamePath, kvp.Key.Replace('/', '\\'))] = kvp.Value; @@ -276,6 +276,8 @@ private void RestoreStatsFiles(string gamePath, BackupManager backupManager, Fil if (kvp.Key.StartsWith("MAPS/", StringComparison.OrdinalIgnoreCase) && kvp.Key.EndsWith("team.dat", StringComparison.OrdinalIgnoreCase)) { string destPath = Path.Combine(gamePath, kvp.Key.Replace('/', '\\')); + // 與套用路徑一致:地圖已被玩家移除時不重建其 team.dat。 + if (!File.Exists(destPath)) continue; byte[] patchedBytes = TeamDatPatcher.GetPatchedBytes(kvp.Value, new TeamDatOptions(false)); SafeWriteAllBytes(destPath, patchedBytes, rollback); _logger.Log(string.Format(Loc.Get("SvcLogRestoredPopulation"), destPath)); @@ -304,9 +306,14 @@ private byte[] GetPatchedObjdefBytes(BackupManager backupManager, PatchProfile o return ObjdefFeaturePatcher.Build(backupManager, options); } - private Dictionary GetPatchedTeamDatBytes(BackupManager backupManager, bool maxPopulation, bool romanEndless) + private Dictionary GetPatchedTeamDatBytes(string gamePath, BackupManager backupManager, bool maxPopulation, bool romanEndless) { var results = MaxPopulationFeature.Build(backupManager, maxPopulation, romanEndless); + // 備份中可能保有玩家已自行移除的地圖;只改寫磁碟上仍存在的 team.dat,不重建已刪除的地圖檔。 + foreach (string key in results.Keys.Where(k => !File.Exists(Path.Combine(gamePath, k.Replace('/', '\\')))).ToList()) + { + results.Remove(key); + } _logger.Log(maxPopulation ? string.Format(Loc.Get("SvcLogTeamDatApplied"), TeamDatPatcher.DefaultPopulationLimit, results.Count) : Loc.Get("SvcLogTeamDatRestored")); int endlessCount = results.Keys.Count(key => key.StartsWith("MAPS/ENDL_", StringComparison.OrdinalIgnoreCase)); _logger.Log(romanEndless