角色能力,存档

This commit is contained in:
2026-05-19 11:50:21 +08:00
parent d25f237e76
commit 2dcb7a961a
136 changed files with 36035 additions and 27551 deletions

View File

@@ -5,17 +5,48 @@ using BaseGames.Core.Save;
namespace BaseGames.World
{
public class SavePoint : MonoBehaviour, IInteractable, ISaveable
/// <summary>
/// 存档点(也是复活点)。
/// 玩家互动后:恢复 HP → 写入存档(场景、出生点 ID、世界状态→ 广播激活事件。
/// 传送点(快速旅行面板)是独立的 TeleportStation 组件,不在此处处理。
///
/// 继承 SaveableMonoBehaviour 以自动向 ISaveableRegistry 注册/注销,
/// 确保 GameSaveManager 在 SaveAsync/LoadAsync 时回调本组件。
///
/// OnSave 仅在 _isActivated=true 时写入复活字段,
/// 防止自动存档期间未激活的存档点覆盖玩家的正确复活位置。
/// </summary>
public class SavePoint : SaveableMonoBehaviour, IInteractable
{
[Header("Config")]
[SerializeField] private string _savePointId;
[SerializeField] private bool _restoreSpring = true;
[Header("Event Channels")]
[SerializeField] private StringEventChannelSO _onSavePointActivated;
[SerializeField] private VoidEventChannelSO _onFastTravelOpen;
[Header("Event Channels - Listen")]
[Tooltip("EVT_SceneLoaded — 用于追踪当前场景名,写入 Player.Scene")]
[SerializeField] private StringEventChannelSO _onSceneLoaded;
private bool _isActivated;
[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;
@@ -24,6 +55,7 @@ namespace BaseGames.World
public void Interact(Transform player)
{
_isActivated = true;
var restorer = player.GetComponentInChildren<IRestoreOnSave>();
if (restorer != null)
{
@@ -32,27 +64,43 @@ namespace BaseGames.World
}
_onSavePointActivated?.Raise(_savePointId);
_onFastTravelOpen?.Raise();
// 触发存档OnSave() 由 SaveAsync 回调所有 ISaveable包含本组件
var svc = ServiceLocator.GetOrDefault<ISaveService>();
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 void OnSave(SaveData data)
public override void OnSave(SaveData data)
{
if (_isActivated && !string.IsNullOrEmpty(_savePointId)
// 仅在本存档点已激活(玩家坐过)时更新复活字段。
// 自动存档也会触发此方法,必须守卫以防止覆盖玩家真正的复活位置。
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 void OnLoad(SaveData data)
public override void OnLoad(SaveData data)
{
_isActivated = !string.IsNullOrEmpty(_savePointId)
&& data.World.ActivatedSavePoints.Contains(_savePointId);
}
}
}