Files
zeling_v2/Assets/_Game/Scripts/World/WorldStateRegistry.cs
2026-05-20 15:10:35 +08:00

115 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using BaseGames.Core.Save;
using UnityEngine;
namespace BaseGames.World
{
/// <summary>世界对象的分类枚举,作为 WorldStateRegistry 的泛化 key。</summary>
public enum WorldObjectCategory
{
Collectible,
SavePoint,
Door,
Destroyed,
Flag,
}
/// <summary>
/// 运行时世界状态缓存。ScriptableObject通过 [SerializeField] 注入各组件。
/// WorldStateRegistrySaverISaveable负责将本对象与存档管道连接
/// SaveAsync → WorldStateRegistrySaver.OnSave → 写出全部分类到 SaveData
/// LoadAsync → WorldStateRegistrySaver.OnLoad → 调用 LoadFromSave(data) 恢复缓存。
/// </summary>
[CreateAssetMenu(menuName = "BaseGames/World/WorldStateRegistry")]
public class WorldStateRegistry : ScriptableObject
{
// ── 统一状态字典 ─────────────────────────────────────────────────────
private readonly Dictionary<WorldObjectCategory, HashSet<string>> _states = new();
/// <summary>
/// 状态变更时广播:(类别, id)。UI / 测试代码可订阅此事件做响应式刷新。
/// </summary>
public event Action<WorldObjectCategory, string> OnStateChanged;
/// <summary>
/// Editor 重新进入 Play Mode 时 ScriptableObject 保留上一次运行的状态,
/// OnEnable 在域重载Domain Reload和每次 Play 开始时都会调用,确保状态干净。
/// </summary>
private void OnEnable() => _states.Clear();
// ── 泛化 API ─────────────────────────────────────────────────────────
/// <summary>检查指定类别中 id 是否已标记。</summary>
public bool IsMarked(WorldObjectCategory category, string id)
=> _states.TryGetValue(category, out var set) && set.Contains(id);
/// <summary>标记指定类别中的 id幂等。</summary>
public void Mark(WorldObjectCategory category, string id)
{
if (!_states.TryGetValue(category, out var set))
{
set = new HashSet<string>();
_states[category] = set;
}
if (set.Add(id))
OnStateChanged?.Invoke(category, id);
}
// ── 语义化具名 API泛化方法快捷方式───────────────────────────────
public bool IsCollected(string id) => IsMarked(WorldObjectCategory.Collectible, id);
public void MarkCollected(string id) => Mark(WorldObjectCategory.Collectible, id);
public bool IsSavePointActivated(string id) => IsMarked(WorldObjectCategory.SavePoint, id);
public void MarkSavePointActivated(string id) => Mark(WorldObjectCategory.SavePoint, id);
public bool IsDestroyed(string id) => IsMarked(WorldObjectCategory.Destroyed, id);
public void MarkDestroyed(string id) => Mark(WorldObjectCategory.Destroyed, id);
public bool IsDoorOpened(string id) => IsMarked(WorldObjectCategory.Door, id);
public void MarkDoorOpened(string id) => Mark(WorldObjectCategory.Door, id);
public bool HasFlag(string key) => IsMarked(WorldObjectCategory.Flag, key);
public void SetFlag(string key) => Mark(WorldObjectCategory.Flag, key);
public void ClearFlag(string key)
{
if (_states.TryGetValue(WorldObjectCategory.Flag, out var set) && set.Remove(key))
OnStateChanged?.Invoke(WorldObjectCategory.Flag, key);
}
// ── Persistence ───────────────────────────────────────────────────────
/// <summary>
/// 从存档数据恢复全部状态。由 WorldStateRegistrySaver.OnLoad 调用。
/// </summary>
public void LoadFromSave(SaveData data)
{
_states.Clear();
if (data == null) return;
foreach (var id in data.World.CollectedIds) Mark(WorldObjectCategory.Collectible, id);
foreach (var id in data.World.ActivatedSavePoints) Mark(WorldObjectCategory.SavePoint, id);
foreach (var id in data.World.OpenedDoors) Mark(WorldObjectCategory.Door, id);
foreach (var id in data.World.DestroyedObjectIds) Mark(WorldObjectCategory.Destroyed, id);
if (data.EventChains?.WorldFlags != null)
foreach (var kv in data.EventChains.WorldFlags)
if (kv.Value) Mark(WorldObjectCategory.Flag, kv.Key);
}
/// <summary>
/// 返回指定分类中所有已标记的 ID快照副本
/// 由 WorldStateRegistrySaver.OnSave 调用,将运行时状态写入 SaveData。
/// </summary>
public IReadOnlyCollection<string> GetAllIds(WorldObjectCategory category)
{
if (_states.TryGetValue(category, out var set))
return new HashSet<string>(set);
return System.Array.Empty<string>();
}
/// <summary>重置所有状态(开始新游戏时调用)。</summary>
public void Reset() => _states.Clear();
}
}