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;
[Tooltip("成就授予凹槽时发布;EquipmentManager 订阅后调用 IncreaseNotches(1)。")]
[SerializeField] private VoidEventChannelSO _onNotchGranted;
// ── 运行时状态 ─────────────────────────────────────────────────────────
private readonly Dictionary _states = new();
private void Awake()
{
if (ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; }
ServiceLocator.Register(this);
InitStates();
}
private void OnEnable() => ServiceLocator.GetOrDefault()?.Register(this);
private void OnDisable() => ServiceLocator.GetOrDefault()?.Unregister(this);
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;
}
// 加载后立即重新评估所有未解锁成就,避免离线满足条件的成就须等到下次游戏事件才触发。
EvaluateAll(saveData);
}
// ── 轮询检查 ──────────────────────────────────────────────────────────
///
/// 使用最新存档数据检查所有未解锁成就的条件。
/// 由 SaveManager.AfterLoad 或外部定期调用(例如每次进入房间)。
///
public void EvaluateAll(SaveData 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
// 若成就授予凹槽,通过事件通知 EquipmentManager 更新运行时状态;
// 不直接写 SaveData,避免与 EquipmentManager.OnSave 的数据竞争。
if (state.Achievement.grantsNotch)
_onNotchGranted?.Raise();
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;
}
}