84 lines
4.0 KiB
C#
84 lines
4.0 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Events;
|
||
using BaseGames.Core.Save;
|
||
|
||
namespace BaseGames.Progression
|
||
{
|
||
/// <summary>
|
||
/// 图鉴追踪服务(仿 <see cref="BaseGames.Equipment.EquipmentManager"/> 的 ISaveable + ServiceLocator 模式)。
|
||
/// 订阅 EVT_EnemyDied(payload=enemyId),记录已发现敌人与击杀次数,持久化到 <see cref="SaveData.Journal"/>。
|
||
/// 仅依赖字符串 enemyId 与事件频道,不引用敌人程序集(asmdef 解耦);敌人代码零改动。
|
||
/// 挂在 Persistent 的 [GameManagers] 下,OnEnable 注册 IBestiaryService 与 ISaveableRegistry。
|
||
/// </summary>
|
||
public class BestiaryService : MonoBehaviour, ISaveable, IBestiaryService
|
||
{
|
||
[Header("事件频道")]
|
||
[Tooltip("EVT_EnemyDied:payload = enemyId。敌人 / Boss 死亡时由 EnemyBase 广播。")]
|
||
[SerializeField] private StringEventChannelSO _onEnemyDied;
|
||
[Tooltip("EVT_BestiaryUpdated:payload = enemyId。新发现 / 击杀计数更新时广播,驱动图鉴 UI 实时刷新。")]
|
||
[SerializeField] private StringEventChannelSO _onBestiaryUpdated;
|
||
|
||
private readonly HashSet<string> _discovered = new();
|
||
private readonly Dictionary<string, int> _killCounts = new();
|
||
private readonly CompositeDisposable _subs = new();
|
||
|
||
// ── IBestiaryService ─────────────────────────────────────────────────
|
||
public int DiscoveredCount => _discovered.Count;
|
||
|
||
public bool IsDiscovered(string enemyId)
|
||
=> !string.IsNullOrEmpty(enemyId) && _discovered.Contains(enemyId);
|
||
|
||
public int GetKillCount(string enemyId)
|
||
=> !string.IsNullOrEmpty(enemyId) && _killCounts.TryGetValue(enemyId, out var n) ? n : 0;
|
||
|
||
// ── 生命周期 ──────────────────────────────────────────────────────────
|
||
private void OnEnable()
|
||
{
|
||
ServiceLocator.Register<IBestiaryService>(this);
|
||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Register(this);
|
||
_onEnemyDied?.Subscribe(OnEnemyDied).AddTo(_subs);
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
ServiceLocator.Unregister<IBestiaryService>(this);
|
||
ServiceLocator.GetOrDefault<ISaveableRegistry>()?.Unregister(this);
|
||
_subs.Clear();
|
||
}
|
||
|
||
// ── 解锁处理 ──────────────────────────────────────────────────────────
|
||
private void OnEnemyDied(string enemyId)
|
||
{
|
||
if (string.IsNullOrEmpty(enemyId)) return;
|
||
_discovered.Add(enemyId);
|
||
_killCounts.TryGetValue(enemyId, out var n);
|
||
_killCounts[enemyId] = n + 1;
|
||
_onBestiaryUpdated?.Raise(enemyId);
|
||
}
|
||
|
||
// ── ISaveable ─────────────────────────────────────────────────────────
|
||
public void OnSave(SaveData data)
|
||
{
|
||
var j = data.Journal;
|
||
j.DiscoveredEnemyIds.Clear();
|
||
foreach (var id in _discovered) j.DiscoveredEnemyIds.Add(id);
|
||
j.EnemyKillCounts.Clear();
|
||
foreach (var kv in _killCounts) j.EnemyKillCounts[kv.Key] = kv.Value;
|
||
}
|
||
|
||
public void OnLoad(SaveData data)
|
||
{
|
||
_discovered.Clear();
|
||
_killCounts.Clear();
|
||
var j = data.Journal;
|
||
if (j == null) return;
|
||
if (j.DiscoveredEnemyIds != null)
|
||
foreach (var id in j.DiscoveredEnemyIds) _discovered.Add(id);
|
||
if (j.EnemyKillCounts != null)
|
||
foreach (var kv in j.EnemyKillCounts) _killCounts[kv.Key] = kv.Value;
|
||
}
|
||
}
|
||
}
|