using System.Collections.Generic; using UnityEngine; using BaseGames.Core; using BaseGames.Core.Save; using BaseGames.Platform; namespace BaseGames.Progression { /// /// 成就运行时状态(不存档,仅运行时追踪)。 /// public class AchievementRuntimeState { public AchievementSO Achievement; public bool IsUnlocked; public float Progress; // 0-1,用于 UI 进度条 } /// /// 成就管理器(架构 16_SupportingModules §2.3)。 /// 负责加载所有 AchievementSO、轮询条件、解锁成就并同步到平台服务。 /// public class AchievementManager : MonoBehaviour, ISaveable, IAchievementService { [Header("成就资产")] [Tooltip("将所有 AchievementSO 拖入此列表")] [SerializeField] private AchievementSO[] _achievements; [Header("事件频道")] [SerializeField] private AchievementEventChannelSO _onAchievementUnlocked; // ── 运行时状态 ───────────────────────────────────────────────────────── private readonly Dictionary _states = new(); private SaveData _saveRef; private void Awake() { if (ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; } ServiceLocator.Register(this); InitStates(); } private void OnDestroy() { ServiceLocator.Unregister(this); } private void InitStates() { _states.Clear(); if (_achievements == null) return; foreach (var ach in _achievements) { if (ach == null || string.IsNullOrEmpty(ach.achievementId)) continue; _states[ach.achievementId] = new AchievementRuntimeState { Achievement = ach, IsUnlocked = false, Progress = 0f, }; } } // ── ISaveable ───────────────────────────────────────────────────────── public void OnSave(SaveData saveData) { if (saveData?.Achievements == null) return; saveData.Achievements.Unlocked.Clear(); saveData.Achievements.Progress.Clear(); foreach (var kv in _states) { if (kv.Value.IsUnlocked) saveData.Achievements.Unlocked.Add(kv.Key); else if (kv.Value.Progress > 0f) saveData.Achievements.Progress[kv.Key] = new AchievementProgress { Percent = kv.Value.Progress }; } } public void OnLoad(SaveData saveData) { if (saveData?.Achievements == null) return; foreach (var id in saveData.Achievements.Unlocked) { if (_states.TryGetValue(id, out var state)) state.IsUnlocked = true; } foreach (var kv in saveData.Achievements.Progress) { if (_states.TryGetValue(kv.Key, out var state)) state.Progress = kv.Value.Percent; } } // ── 轮询检查 ────────────────────────────────────────────────────────── /// /// 使用最新存档数据检查所有未解锁成就的条件。 /// 由 SaveManager.AfterLoad 或外部定期调用(例如每次进入房间)。 /// public void EvaluateAll(SaveData save) { _saveRef = save; foreach (var state in _states.Values) { if (state.IsUnlocked) continue; EvaluateSingle(state, save); } } private void EvaluateSingle(AchievementRuntimeState state, SaveData save) { if (state.Achievement.conditions == null || state.Achievement.conditions.Length == 0) return; bool allMet = true; float totalProgress = 0f; foreach (var cond in state.Achievement.conditions) { if (cond == null) continue; if (!cond.IsMet(save)) { allMet = false; } totalProgress += cond.GetProgress(save); } int condCount = state.Achievement.conditions.Length; state.Progress = condCount > 0 ? totalProgress / condCount : 0f; if (allMet) Unlock(state); } // ── 主动解锁 ────────────────────────────────────────────────────────── /// 直接解锁成就(用于剧情触发等无条件解锁场景)。 public void UnlockById(string achievementId) { if (_states.TryGetValue(achievementId, out var state) && !state.IsUnlocked) Unlock(state); } private void Unlock(AchievementRuntimeState state) { state.IsUnlocked = true; state.Progress = 1f; _onAchievementUnlocked?.Raise(state.Achievement); #if STEAMWORKS_NET ServiceLocator.Get()?.UnlockAchievement(state.Achievement.achievementId); #endif // 若成就授予凹槽,写入存档数值 if (state.Achievement.grantsNotch && _saveRef != null) { _saveRef.Equipment.MaxNotches++; } Debug.Log($"[Achievement] 解锁:{state.Achievement.displayName} ({state.Achievement.achievementId})"); } // ── 查询接口 ────────────────────────────────────────────────────────── public bool IsUnlocked(string id) => _states.TryGetValue(id, out var s) && s.IsUnlocked; public float GetProgress(string id) => _states.TryGetValue(id, out var s) ? s.Progress : 0f; public IEnumerable GetAllStates() => _states.Values; } }