using UnityEngine; using BaseGames.Core; using BaseGames.Core.Events; using BaseGames.Core.Save; namespace BaseGames.Progression { /// /// 进程锁(架构 09_ProgressionModule §12)。 /// 单向/永久性阻挡,需满足特定条件(击败 Boss)才能解锁。 /// 通过 ServiceLocator.GetOrDefault<SaveManager>() 读取进度,订阅 BossDefeated 事件实时响应。 /// public class ProgressLock : MonoBehaviour, ISaveable { [Header("解锁条件")] [SerializeField] private string _requiredBossId; // 空 = 不检查 Boss [SerializeField] private string _requiredItemId; // 空 = 不检查道具(P1 预留) [Header("物理表现")] [SerializeField] private GameObject _lockedVisuals; // 锁住状态视觉 [SerializeField] private GameObject _unlockedVisuals; // 开启状态视觉(可 null) [SerializeField] private Collider2D _blockCollider; [Header("存档")] [SerializeField] private string _lockId; // 唯一 ID,存档记录开启状态 [Header("Event Channels")] [SerializeField] private StringEventChannelSO _onBossDefeated; // EVT_BossDefeated private readonly CompositeDisposable _subs = new(); private bool _isUnlocked; private void Awake() { ServiceLocator.GetOrDefault()?.Register(this); } private void OnDestroy() { ServiceLocator.GetOrDefault()?.Unregister(this); } private void Start() { ApplyState(CheckUnlocked()); } private void OnEnable() { _onBossDefeated?.Subscribe(OnBossDefeated).AddTo(_subs); } private void OnDisable() { _subs.Clear(); } private void OnBossDefeated(string bossId) { if (!string.IsNullOrEmpty(_requiredBossId) && bossId != _requiredBossId) return; if (CheckUnlocked()) ApplyState(true); } // ── ISaveable ───────────────────────────────────────────────────────── public void OnSave(SaveData data) { if (_isUnlocked && !string.IsNullOrEmpty(_lockId) && !data.World.OpenedDoors.Contains(_lockId)) data.World.OpenedDoors.Add(_lockId); } public void OnLoad(SaveData data) { // 状态由 CheckUnlocked() 读取 IsDoorOpened(从 SaveData.OpenedDoors),应于 Start() 和 OnBossDefeated() 重新评估 } private bool CheckUnlocked() { var sm = ServiceLocator.GetOrDefault(); if (sm == null) return false; if (!string.IsNullOrEmpty(_requiredBossId) && !sm.IsBossDefeated(_requiredBossId)) return false; return string.IsNullOrEmpty(_lockId) || sm.IsDoorOpened(_lockId); } private void ApplyState(bool unlocked) { _isUnlocked = unlocked; if (_blockCollider != null) _blockCollider.enabled = !unlocked; if (_lockedVisuals != null) _lockedVisuals.SetActive(!unlocked); if (_unlockedVisuals != null) _unlockedVisuals.SetActive(unlocked); } } }