using UnityEngine; using BaseGames.Core; using BaseGames.Core.Events; using BaseGames.Core.Save; namespace BaseGames.World { /// /// 存档点(也是复活点)。 /// 玩家互动后:恢复 HP → 写入存档(场景、出生点 ID、世界状态)→ 广播激活事件。 /// 传送点(快速旅行面板)是独立的 TeleportStation 组件,不在此处处理。 /// /// 继承 SaveableMonoBehaviour 以自动向 ISaveableRegistry 注册/注销, /// 确保 GameSaveManager 在 SaveAsync/LoadAsync 时回调本组件。 /// /// OnSave 仅在 _isActivated=true 时写入复活字段, /// 防止自动存档期间未激活的存档点覆盖玩家的正确复活位置。 /// public class SavePoint : SaveableMonoBehaviour, IInteractable { [Header("Config")] [SerializeField] private string _savePointId; [SerializeField] private bool _restoreSpring = true; [Header("Event Channels - Listen")] [Tooltip("EVT_SceneLoaded — 用于追踪当前场景名,写入 Player.Scene")] [SerializeField] private StringEventChannelSO _onSceneLoaded; [Header("Event Channels - Raise")] [SerializeField] private StringEventChannelSO _onSavePointActivated; private bool _isActivated; private string _currentSceneName; private readonly CompositeDisposable _subs = new(); protected override void OnEnable() { base.OnEnable(); // 向 ISaveableRegistry 注册,确保 OnSave/OnLoad 被回调 _onSceneLoaded?.Subscribe(OnSceneLoaded).AddTo(_subs); } protected override void OnDisable() { base.OnDisable(); // 从 ISaveableRegistry 注销 _subs.Clear(); } private void OnSceneLoaded(string sceneName) => _currentSceneName = sceneName; // ── IInteractable ────────────────────────────────────────────────────── public bool CanInteract => true; public string InteractPrompt => _isActivated ? "休息" : "激活"; public void Interact(Transform player) { _isActivated = true; var restorer = player.GetComponentInChildren(); if (restorer != null) { restorer.FullRestore(); if (_restoreSpring) restorer.RestoreSpring(); } _onSavePointActivated?.Raise(_savePointId); // 触发存档:OnSave() 由 SaveAsync 回调所有 ISaveable,包含本组件 var svc = ServiceLocator.GetOrDefault(); if (svc != null) _ = svc.SaveAsync(svc.ActiveSlot); else Debug.LogWarning("[SavePoint] ISaveService 未注册,跳过存档。", this); } public void OnPlayerEnterRange(Transform player) { } public void OnPlayerExitRange() { } // ── ISaveable(通过 SaveableMonoBehaviour 注册)──────────────────────── public bool IsActivated => _isActivated; public void SetActivated(bool val) => _isActivated = val; public override void OnSave(SaveData data) { // 仅在本存档点已激活(玩家坐过)时更新复活字段。 // 自动存档也会触发此方法,必须守卫以防止覆盖玩家真正的复活位置。 if (!_isActivated) return; if (!string.IsNullOrEmpty(_currentSceneName)) data.Player.Scene = _currentSceneName; if (!string.IsNullOrEmpty(_savePointId)) data.Meta.SavePointId = _savePointId; if (!string.IsNullOrEmpty(_savePointId) && !data.World.ActivatedSavePoints.Contains(_savePointId)) data.World.ActivatedSavePoints.Add(_savePointId); } public override void OnLoad(SaveData data) { _isActivated = !string.IsNullOrEmpty(_savePointId) && data.World.ActivatedSavePoints.Contains(_savePointId); } } }