97 lines
3.4 KiB
C#
97 lines
3.4 KiB
C#
using UnityEngine;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Events;
|
||
using BaseGames.Core.Save;
|
||
|
||
namespace BaseGames.Progression
|
||
{
|
||
/// <summary>
|
||
/// 进程锁(架构 09_ProgressionModule §12)。
|
||
/// 单向/永久性阻挡,需满足特定条件(击败 Boss)才能解锁。
|
||
/// 通过 ServiceLocator.GetOrDefault<SaveManager>() 读取进度,订阅 BossDefeated 事件实时响应。
|
||
/// </summary>
|
||
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<ISaveableRegistry>()?.Register(this);
|
||
}
|
||
|
||
private void OnDestroy()
|
||
{
|
||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.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<ISaveService>();
|
||
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);
|
||
}
|
||
}
|
||
}
|