using System;
using System.Threading.Tasks;
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)
RunFireAndForget(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);
}
private static async void RunFireAndForget(Task task)
{
try { await task; }
catch (Exception e)
{
Debug.LogError($"[SavePoint] 存档失败: {e.Message}\n{e.StackTrace}");
}
}
}
}