using System; using System.Collections.Generic; using UnityEngine; [Serializable] public struct EnemyConfiguration { public int enemyID; [Range(0, 100)] public int hpPercentage; } [Serializable] public class ChartFileEntry { [Header("Inspector")] public int difficulty; public float difficultyLEVEL; [Header("Inspector")] public float enemyTotalHP_Multiplier; [Header("Enemy Configuration")] [Tooltip("List of enemy configurations for this difficulty (0-5 enemies)")] public List enemyConfigList = new List(); // [Tooltip("List of enemy IDs for this difficulty (0-5 enemies)")] // public List enemyList = new List(); // Deprecated [Header("Inspector")] public string difficultyName; [Header("Inspector")] public TextAsset chartFile; [Header("Inspector")] [TextArea(3, 10)] public string difficultyDescription; [Header("Inspector")] public int lastScoreForThisDifficulty; [Header("Inspector")] public int chartPersonalRecordForThisDifficulty; public int idolPersonalRecordForThisDifficulty; public int totalPersonalRecordForThisDifficulty; [Header("Inspector")] public float levelProgressForThisDifficulty; [Header("Inspector")] public DateTime lastPlayTimeForThisDifficulty; [Header("Inspector")] public int experienceProvidedForThisDifficulty; } [CreateAssetMenu(fileName = "NewSongData", menuName = "EaseOut/SongData")] public class SongData : ScriptableObject { public string usernameID = "user_TestUser@FloatGaming"; public string songName; public int songID; public int songLength_second; public string artistName; public string painter; public string level_creator; public string belongsTo_whichDLC; public int bpm; public float songDuration; public int game_enterTimes; public float time_totalPlayingTime; public DateTime uploadData; [Header("Inspector")] public Sprite illustration; public Sprite backgroudPIC; public Sprite fullscreen_songPicture; public Sprite card_profileImage; [Header("Inspector")] public Sprite right_pic_bottom_image; public Sprite right_pic_top_image; [Header("Inspector")] public AudioClip audioFile; public AudioClip audio_previewAudio; [Header("Inspector")] public int difficultyID; public Dictionary difficultyNumberMap = new Dictionary(); public Dictionary personalRecordMap = new Dictionary(); public Dictionary idolRecordMap = new Dictionary(); public Dictionary chartScoreMap = new Dictionary(); public Dictionary levelProgressMap = new Dictionary(); public Dictionary chartFileMap = new Dictionary(); public List chartFiles = new List(); [NonSerialized] private Dictionary> enemyConfigSignatureCache; public int personalRecord; public float current_levelProgress; public bool _if_level_is_EASE; public int max_comboRecord; [Header("Inspector")] public int thisLevel_selectedDifficultyID; [Header("Inspector")] public string thisLevel_addDateString; [Header("Inspector")] [TextArea(3, 10)] public string thisLevel_overallDescription; private bool _isPersistentDataLoaded = false; private void OnValidate() { if (enemyConfigSignatureCache == null) enemyConfigSignatureCache = new Dictionary>(); if (chartFiles != null) { foreach (var chart in chartFiles) { if (chart != null) { if (chart.enemyConfigList != null && chart.enemyConfigList.Count > 5) { chart.enemyConfigList.RemoveRange(5, chart.enemyConfigList.Count - 5); Debug.LogWarning($"[SongData] {songName} (Difficulty {chart.difficulty}): enemyConfigList length restricted to a maximum of 5."); } if (chart.enemyConfigList != null && chart.enemyConfigList.Count > 0) { var activeIndices = new List(); var currentSignature = new List(); for (int i = 0; i < chart.enemyConfigList.Count; i++) { var c = chart.enemyConfigList[i]; if (c.enemyID == 0) { if (c.hpPercentage != 0) { c.hpPercentage = 0; chart.enemyConfigList[i] = c; } currentSignature.Add(0L); continue; } if (c.hpPercentage < 0) c.hpPercentage = 0; if (c.hpPercentage > 100) c.hpPercentage = 100; chart.enemyConfigList[i] = c; activeIndices.Add(i); currentSignature.Add(((long)c.enemyID << 32) | (uint)c.hpPercentage); } int activeCount = activeIndices.Count; if (activeCount == 1) { int idx = activeIndices[0]; var c = chart.enemyConfigList[idx]; c.hpPercentage = 100; chart.enemyConfigList[idx] = c; } else if (activeCount > 1) { int changedIndex = -1; int changedEnemyID = 0; if (enemyConfigSignatureCache.TryGetValue(chart.difficulty, out var oldSignature) && oldSignature != null && oldSignature.Count == currentSignature.Count) { for (int i = 0; i < currentSignature.Count; i++) { if (oldSignature[i] != currentSignature[i]) { changedIndex = i; changedEnemyID = chart.enemyConfigList[i].enemyID; break; } } } int remainderIndex = activeIndices[activeIndices.Count - 1]; if (changedIndex >= 0) { int candidate = -1; for (int i = activeIndices.Count - 1; i >= 0; i--) { int idx = activeIndices[i]; if (idx == changedIndex) continue; if (chart.enemyConfigList[idx].enemyID != changedEnemyID) { candidate = idx; break; } } if (candidate == -1) { for (int i = activeIndices.Count - 1; i >= 0; i--) { int idx = activeIndices[i]; if (idx == changedIndex) continue; candidate = idx; break; } } if (candidate != -1) remainderIndex = candidate; } int sumOthers = 0; for (int i = 0; i < activeIndices.Count; i++) { int idx = activeIndices[i]; if (idx == remainderIndex) continue; sumOthers += chart.enemyConfigList[idx].hpPercentage; } int overflow = sumOthers - 100; if (overflow > 0) { for (int i = activeIndices.Count - 1; i >= 0 && overflow > 0; i--) { int idx = activeIndices[i]; if (idx == remainderIndex) continue; if (idx == changedIndex) continue; var c = chart.enemyConfigList[idx]; int reduce = Mathf.Min(overflow, c.hpPercentage); if (reduce > 0) { c.hpPercentage -= reduce; overflow -= reduce; chart.enemyConfigList[idx] = c; } } if (overflow > 0 && changedIndex >= 0 && changedIndex != remainderIndex) { var c = chart.enemyConfigList[changedIndex]; int reduce = Mathf.Min(overflow, c.hpPercentage); if (reduce > 0) { c.hpPercentage -= reduce; overflow -= reduce; chart.enemyConfigList[changedIndex] = c; } } } sumOthers = 0; for (int i = 0; i < activeIndices.Count; i++) { int idx = activeIndices[i]; if (idx == remainderIndex) continue; sumOthers += chart.enemyConfigList[idx].hpPercentage; } int remainder = 100 - sumOthers; if (remainder < 0) remainder = 0; if (remainder > 100) remainder = 100; var r = chart.enemyConfigList[remainderIndex]; r.hpPercentage = remainder; chart.enemyConfigList[remainderIndex] = r; } currentSignature.Clear(); for (int i = 0; i < chart.enemyConfigList.Count; i++) { var c = chart.enemyConfigList[i]; if (c.enemyID == 0) currentSignature.Add(0L); else currentSignature.Add(((long)c.enemyID << 32) | (uint)c.hpPercentage); } enemyConfigSignatureCache[chart.difficulty] = currentSignature; } } } } } private void OnEnable() { if (difficultyNumberMap == null) difficultyNumberMap = new Dictionary(); if (personalRecordMap == null) personalRecordMap = new Dictionary(); if (idolRecordMap == null) idolRecordMap = new Dictionary(); if (chartScoreMap == null) chartScoreMap = new Dictionary(); if (levelProgressMap == null) levelProgressMap = new Dictionary(); if (chartFileMap == null) chartFileMap = new Dictionary(); if (chartFiles == null) chartFiles = new List(); // 尝试从持久化存储加载数据 LoadPersistent(); SyncRecordsFromChartFiles(); SyncChartFileMap(); } public int CalculateHighestPersonalRecord() { if (personalRecordMap == null) personalRecordMap = new Dictionary(); if (idolRecordMap == null) idolRecordMap = new Dictionary(); int highest = 0; foreach (var kvp in personalRecordMap) { int diff = kvp.Key; int p = kvp.Value; int i = idolRecordMap.ContainsKey(diff) ? idolRecordMap[diff] : 0; int combined = p + i; if (combined > highest) highest = combined; } personalRecord = highest; return highest; } /// /// 将数据保存到持久化存储,并附加 MD5 校验和防止篡改。 /// public void SavePersistent() { if (songID == 0) { Debug.LogWarning($"[SongData] {songName} has songID=0. Saving to 'SongData_0.json' may overwrite other songs!"); } try { // 在保存前确保 chartFiles 已同步,这样在编辑器中查看 SO 时数据也是最新的 SyncEntriesFromMaps(); string path = System.IO.Path.Combine(Application.persistentDataPath, $"SongData_{songID}.json"); SongDataSerializable serializable = new SongDataSerializable(this); // 1. 序列化基础数据 string jsonWithoutHash = JsonUtility.ToJson(serializable); // 2. 计算 MD5 (使用自定义盐值提高安全性) serializable.dataHash = CalculateMD5(jsonWithoutHash + "bansonic2026@fudongyouxi"); // 3. 再次序列化包含 Hash 的最终数据 string finalJson = JsonUtility.ToJson(serializable, true); // 确保目录存在 string dir = System.IO.Path.GetDirectoryName(path); if (!System.IO.Directory.Exists(dir)) System.IO.Directory.CreateDirectory(dir); System.IO.File.WriteAllText(path, finalJson); Debug.Log($"[SongData] {songName} (ID:{songID}) saved to {path}"); #if UNITY_EDITOR if (!Application.isPlaying) { UnityEditor.EditorUtility.SetDirty(this); UnityEditor.AssetDatabase.SaveAssets(); } #endif } catch (System.Exception e) { Debug.LogError($"[SongData] Save failed: {e.Message}"); } } /// /// 从持久化 JSON 恢复数据。 /// 校验逻辑已迁移至 DataCheck 脚本,此处仅执行基础加载。 /// public void LoadPersistent() { if (_isPersistentDataLoaded) return; string path = System.IO.Path.Combine(Application.persistentDataPath, $"SongData_{songID}.json"); if (System.IO.File.Exists(path)) { try { string json = System.IO.File.ReadAllText(path); // 尝试解析数据 SongDataSerializable serializable = JsonUtility.FromJson(json); if (serializable != null) { serializable.ApplyTo(this); SyncEntriesFromMaps(); _isPersistentDataLoaded = true; Debug.Log($"[SongData] {songName} (ID:{songID}) data restored from persistent storage."); } } catch (System.Exception e) { Debug.LogWarning($"[SongData] {songName} 基础加载失败: {e.Message}"); } } else { // 如果不存在存档文件,也标记为已加载,防止重复检查 _isPersistentDataLoaded = true; } } public void ClearSaveDataAndReload() { if (personalRecordMap != null) personalRecordMap.Clear(); else personalRecordMap = new Dictionary(); if (idolRecordMap != null) idolRecordMap.Clear(); else idolRecordMap = new Dictionary(); if (chartScoreMap != null) chartScoreMap.Clear(); else chartScoreMap = new Dictionary(); if (levelProgressMap != null) levelProgressMap.Clear(); else levelProgressMap = new Dictionary(); if (chartFiles != null) { for (int i = 0; i < chartFiles.Count; i++) { ChartFileEntry entry = chartFiles[i]; if (entry == null) continue; entry.lastScoreForThisDifficulty = 0; entry.chartPersonalRecordForThisDifficulty = 0; entry.idolPersonalRecordForThisDifficulty = 0; entry.totalPersonalRecordForThisDifficulty = 0; entry.levelProgressForThisDifficulty = 0f; int diff = entry.difficulty; personalRecordMap[diff] = 0; idolRecordMap[diff] = 0; chartScoreMap[diff] = 0; levelProgressMap[diff] = 0f; } } if (difficultyNumberMap != null && difficultyNumberMap.Count > 0) { foreach (var kvp in difficultyNumberMap) { int diff = kvp.Key; if (!personalRecordMap.ContainsKey(diff)) personalRecordMap[diff] = 0; if (!idolRecordMap.ContainsKey(diff)) idolRecordMap[diff] = 0; if (!chartScoreMap.ContainsKey(diff)) chartScoreMap[diff] = 0; if (!levelProgressMap.ContainsKey(diff)) levelProgressMap[diff] = 0f; } } game_enterTimes = 0; time_totalPlayingTime = 0f; current_levelProgress = 0f; _if_level_is_EASE = false; max_comboRecord = 0; personalRecord = 0; UpdateOverallRecordsFromEntries(); string path = System.IO.Path.Combine(Application.persistentDataPath, $"SongData_{songID}.json"); if (System.IO.File.Exists(path)) { try { System.IO.File.Delete(path); } catch { } } SavePersistent(); #if UNITY_EDITOR UnityEditor.EditorUtility.SetDirty(this); #endif } /// /// 当检测到篡改或文件损坏时,重置所有动态记录并立即重新保存文件以恢复合法性。 /// private void ResetToZeroAndSave() { // 1. 重置所有动态数值字段 game_enterTimes = 0; time_totalPlayingTime = 0; current_levelProgress = 0; _if_level_is_EASE = false; max_comboRecord = 0; personalRecord = 0; // 2. 清空并确保初始化所有动态字典映射 if (difficultyNumberMap == null) difficultyNumberMap = new Dictionary(); else difficultyNumberMap.Clear(); if (personalRecordMap == null) personalRecordMap = new Dictionary(); else personalRecordMap.Clear(); if (idolRecordMap == null) idolRecordMap = new Dictionary(); else idolRecordMap.Clear(); if (chartScoreMap == null) chartScoreMap = new Dictionary(); else chartScoreMap.Clear(); if (levelProgressMap == null) levelProgressMap = new Dictionary(); else levelProgressMap.Clear(); // 3. 立即调用保存方法,生成一个新的、带有正确哈希值的合法文件 SavePersistent(); Debug.LogWarning($"[SongData] {songName} has been fully reset to zero due to data integrity failure."); } /// /// 静态校验方法,可在异步线程调用,判断 JSON 内容是否合法。 /// public static bool VerifyJsonIntegrity(string json, string salt, out SongDataSerializable result) { result = null; try { SongDataSerializable serializable = JsonUtility.FromJson(json); if (serializable == null) return false; string savedHash = serializable.dataHash; serializable.dataHash = ""; string currentJson = JsonUtility.ToJson(serializable); // 使用静态 MD5 计算 string calculatedHash = StaticCalculateMD5(currentJson + salt); result = serializable; return savedHash == calculatedHash; } catch { return false; } } public static string StaticCalculateMD5(string input) { using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) { byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input); byte[] hashBytes = md5.ComputeHash(inputBytes); return System.BitConverter.ToString(hashBytes).Replace("-", "").ToLower(); } } private string CalculateMD5(string input) { return StaticCalculateMD5(input); } public void UpdateDifficultyRecord(int difficulty, int pmScore, int idolScore) { if (personalRecordMap == null) personalRecordMap = new Dictionary(); if (idolRecordMap == null) idolRecordMap = new Dictionary(); int currentBestTotal = GetPersonalRecord(difficulty) + GetIdolRecord(difficulty); int newTotal = pmScore + idolScore; if (newTotal <= currentBestTotal) return; personalRecordMap[difficulty] = pmScore; idolRecordMap[difficulty] = idolScore; ChartFileEntry entry = chartFiles != null ? chartFiles.Find(e => e != null && e.difficulty == difficulty) : null; if (entry != null) { entry.chartPersonalRecordForThisDifficulty = pmScore; entry.idolPersonalRecordForThisDifficulty = idolScore; entry.totalPersonalRecordForThisDifficulty = newTotal; } CalculateHighestPersonalRecord(); } public void ApplySettlementResult(int difficulty, int pmScore, int idolScore, int maxScoreSum) { if (difficulty < 0) return; if (personalRecordMap == null) personalRecordMap = new Dictionary(); if (idolRecordMap == null) idolRecordMap = new Dictionary(); if (chartScoreMap == null) chartScoreMap = new Dictionary(); if (levelProgressMap == null) levelProgressMap = new Dictionary(); int total = pmScore + idolScore; int currentBestTotal = GetPersonalRecord(difficulty) + GetIdolRecord(difficulty); bool isNewTotalRecord = total > currentBestTotal; if (isNewTotalRecord) { personalRecordMap[difficulty] = pmScore; idolRecordMap[difficulty] = idolScore; } ChartFileEntry entry = chartFiles != null ? chartFiles.Find(e => e != null && e.difficulty == difficulty) : null; if (entry != null) { if (isNewTotalRecord) { entry.chartPersonalRecordForThisDifficulty = pmScore; entry.idolPersonalRecordForThisDifficulty = idolScore; entry.totalPersonalRecordForThisDifficulty = total; } float newProgress = maxScoreSum > 0 ? (float)total / (float)maxScoreSum : 0f; if (newProgress > entry.levelProgressForThisDifficulty) { entry.levelProgressForThisDifficulty = newProgress; } if (!levelProgressMap.ContainsKey(difficulty) || newProgress > levelProgressMap[difficulty]) { levelProgressMap[difficulty] = newProgress; } entry.lastScoreForThisDifficulty = total; } chartScoreMap[difficulty] = total; SyncEntriesFromMaps(); SavePersistent(); } /// /// 将 Dictionary 中的数据同步回 ChartFileEntry 列表,确保 SO 内存数据与持久化数据一致。 /// public void SyncEntriesFromMaps() { if (chartFiles == null) return; foreach (var entry in chartFiles) { if (entry == null) continue; int diff = entry.difficulty; if (personalRecordMap != null && personalRecordMap.ContainsKey(diff)) entry.chartPersonalRecordForThisDifficulty = personalRecordMap[diff]; if (idolRecordMap != null && idolRecordMap.ContainsKey(diff)) entry.idolPersonalRecordForThisDifficulty = idolRecordMap[diff]; if (personalRecordMap != null && personalRecordMap.ContainsKey(diff) && idolRecordMap != null && idolRecordMap.ContainsKey(diff)) entry.totalPersonalRecordForThisDifficulty = personalRecordMap[diff] + idolRecordMap[diff]; else if (personalRecordMap != null && personalRecordMap.ContainsKey(diff)) entry.totalPersonalRecordForThisDifficulty = personalRecordMap[diff]; if (levelProgressMap != null && levelProgressMap.ContainsKey(diff)) entry.levelProgressForThisDifficulty = levelProgressMap[diff]; if (chartScoreMap != null && chartScoreMap.ContainsKey(diff)) entry.lastScoreForThisDifficulty = chartScoreMap[diff]; } UpdateOverallRecordsFromEntries(); } public void GetAbsoluteHighestScore(out int highestScore, out int bestDifficulty, out string bestDifficultyName) { highestScore = 0; bestDifficulty = -1; bestDifficultyName = ""; if (chartFiles != null) { for (int i = 0; i < chartFiles.Count; i++) { ChartFileEntry entry = chartFiles[i]; if (entry == null) continue; int total = GetPersonalRecord(entry.difficulty) + GetIdolRecord(entry.difficulty); if (total <= 0) total = entry.totalPersonalRecordForThisDifficulty; if (total <= 0) total = entry.chartPersonalRecordForThisDifficulty + entry.idolPersonalRecordForThisDifficulty; if (total > highestScore || bestDifficulty == -1) { highestScore = total; bestDifficulty = entry.difficulty; bestDifficultyName = !string.IsNullOrEmpty(entry.difficultyName) ? entry.difficultyName : GetDifficultyDefaultName(entry.difficulty); } } } if (bestDifficulty != -1 || personalRecordMap == null) return; foreach (var kvp in personalRecordMap) { int diff = kvp.Key; int total = kvp.Value + GetIdolRecord(diff); if (total > highestScore || bestDifficulty == -1) { highestScore = total; bestDifficulty = diff; bestDifficultyName = GetDifficultyDefaultName(diff); } } } private string GetDifficultyDefaultName(int difficulty) { switch (difficulty) { case 0: return "Easy"; case 1: return "Hard"; case 2: return "Incredible"; case 3: return "Impossible"; default: return "Unknown"; } } public void UpdatePersonalRecord(int difficulty, int newScore) { if (personalRecordMap == null) personalRecordMap = new Dictionary(); if (!personalRecordMap.ContainsKey(difficulty) || newScore > personalRecordMap[difficulty]) personalRecordMap[difficulty] = newScore; ChartFileEntry entry = chartFiles != null ? chartFiles.Find(e => e != null && e.difficulty == difficulty) : null; if (entry != null) { entry.chartPersonalRecordForThisDifficulty = personalRecordMap[difficulty]; int idol = idolRecordMap != null && idolRecordMap.ContainsKey(difficulty) ? idolRecordMap[difficulty] : 0; entry.totalPersonalRecordForThisDifficulty = personalRecordMap[difficulty] + idol; } CalculateHighestPersonalRecord(); } private void UpdateOverallRecordsFromEntries() { int highestTotal = 0; float highestProgress = 0f; if (chartFiles != null && chartFiles.Count > 0) { for (int i = 0; i < chartFiles.Count; i++) { ChartFileEntry entry = chartFiles[i]; if (entry == null) continue; int total = entry.totalPersonalRecordForThisDifficulty; if (total <= 0) { int p = GetPersonalRecord(entry.difficulty); int idol = GetIdolRecord(entry.difficulty); total = p + idol; } if (total > highestTotal) { highestTotal = total; } if (entry.levelProgressForThisDifficulty > highestProgress) { highestProgress = entry.levelProgressForThisDifficulty; } } personalRecord = highestTotal; current_levelProgress = highestProgress; return; } if (personalRecordMap != null && personalRecordMap.Count > 0) { foreach (var kvp in personalRecordMap) { int diff = kvp.Key; int total = kvp.Value + GetIdolRecord(diff); if (total > highestTotal) highestTotal = total; } personalRecord = highestTotal; } } private void ApplySerializableRoundTrip() { SongDataSerializable serializable = new SongDataSerializable(this); string json = JsonUtility.ToJson(serializable); SongDataSerializable roundTrip = JsonUtility.FromJson(json); if (roundTrip != null) { roundTrip.ApplyTo(this); SyncEntriesFromMaps(); } } public void UpdateIdolRecord(int difficulty, int newIdolScore) { if (idolRecordMap == null) idolRecordMap = new Dictionary(); if (!idolRecordMap.ContainsKey(difficulty) || newIdolScore > idolRecordMap[difficulty]) idolRecordMap[difficulty] = newIdolScore; ChartFileEntry entry = chartFiles != null ? chartFiles.Find(e => e != null && e.difficulty == difficulty) : null; if (entry != null) { entry.idolPersonalRecordForThisDifficulty = idolRecordMap[difficulty]; int personal = personalRecordMap != null && personalRecordMap.ContainsKey(difficulty) ? personalRecordMap[difficulty] : 0; entry.totalPersonalRecordForThisDifficulty = personal + idolRecordMap[difficulty]; } CalculateHighestPersonalRecord(); } public void UpdateChartScore(int difficulty, int newChartScore) { if (chartScoreMap == null) chartScoreMap = new Dictionary(); chartScoreMap[difficulty] = newChartScore; ChartFileEntry entry = chartFiles != null ? chartFiles.Find(e => e != null && e.difficulty == difficulty) : null; if (entry != null) entry.lastScoreForThisDifficulty = newChartScore; } public int GetPersonalRecord(int difficulty) { if (personalRecordMap == null) return 0; return personalRecordMap.TryGetValue(difficulty, out int v) ? v : 0; } public int GetIdolRecord(int difficulty) { if (idolRecordMap == null) return 0; return idolRecordMap.TryGetValue(difficulty, out int v) ? v : 0; } public int GetChartScore(int difficulty) { if (chartScoreMap == null) return 0; return chartScoreMap.TryGetValue(difficulty, out int v) ? v : 0; } public TextAsset GetChartFile(int difficulty) { if (chartFiles != null) { for (int i = 0; i < chartFiles.Count; i++) { ChartFileEntry entry = chartFiles[i]; if (entry != null && entry.difficulty == difficulty) return entry.chartFile; } } if (chartFileMap != null && chartFileMap.TryGetValue(difficulty, out TextAsset mapped)) return mapped; return null; } public void SyncChartFileMap() { if (chartFileMap == null) chartFileMap = new Dictionary(); chartFileMap.Clear(); if (chartFiles == null) return; for (int i = 0; i < chartFiles.Count; i++) { ChartFileEntry entry = chartFiles[i]; if (entry != null && entry.chartFile != null) chartFileMap[entry.difficulty] = entry.chartFile; } } public void SyncRecordsFromChartFiles() { if (personalRecordMap == null) personalRecordMap = new Dictionary(); if (idolRecordMap == null) idolRecordMap = new Dictionary(); if (chartScoreMap == null) chartScoreMap = new Dictionary(); if (levelProgressMap == null) levelProgressMap = new Dictionary(); if (chartFiles == null) return; for (int i = 0; i < chartFiles.Count; i++) { ChartFileEntry entry = chartFiles[i]; if (entry == null) continue; if (!difficultyNumberMap.ContainsKey(entry.difficulty)) difficultyNumberMap[entry.difficulty] = Mathf.RoundToInt(entry.difficultyLEVEL); if (!personalRecordMap.ContainsKey(entry.difficulty) || entry.chartPersonalRecordForThisDifficulty > personalRecordMap[entry.difficulty]) personalRecordMap[entry.difficulty] = entry.chartPersonalRecordForThisDifficulty; if (!idolRecordMap.ContainsKey(entry.difficulty) || entry.idolPersonalRecordForThisDifficulty > idolRecordMap[entry.difficulty]) idolRecordMap[entry.difficulty] = entry.idolPersonalRecordForThisDifficulty; if (!chartScoreMap.ContainsKey(entry.difficulty) || entry.lastScoreForThisDifficulty > chartScoreMap[entry.difficulty]) chartScoreMap[entry.difficulty] = entry.lastScoreForThisDifficulty; if (!levelProgressMap.ContainsKey(entry.difficulty) || entry.levelProgressForThisDifficulty > levelProgressMap[entry.difficulty]) levelProgressMap[entry.difficulty] = entry.levelProgressForThisDifficulty; int p = personalRecordMap.ContainsKey(entry.difficulty) ? personalRecordMap[entry.difficulty] : 0; int idol = idolRecordMap.ContainsKey(entry.difficulty) ? idolRecordMap[entry.difficulty] : 0; entry.totalPersonalRecordForThisDifficulty = p + idol; } CalculateHighestPersonalRecord(); } }