133 lines
5.8 KiB
C#
133 lines
5.8 KiB
C#
using System;
|
||
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using BaseGames.Core;
|
||
using BaseGames.Core.Events;
|
||
using UnityEngine;
|
||
|
||
namespace BaseGames.EventChain
|
||
{
|
||
/// <summary>
|
||
/// 世界事件链管理器(架构 14_NarrativeModule §10)。
|
||
/// 订阅游戏各系统的 SO 事件频道,转发为中继 C# 事件供 ChainCondition.Register() 绑定。
|
||
/// 每当收到新事件时,检查所有链的触发条件。
|
||
/// </summary>
|
||
public class EventChainManager : MonoBehaviour
|
||
{
|
||
[Header("所有事件链")]
|
||
[SerializeField] private EventChainSO[] _chains;
|
||
|
||
[Header("事件频道(中继)")]
|
||
[SerializeField] private StringEventChannelSO _onBossDefeated; // EVT_EnemyDied(bossId)
|
||
[SerializeField] private StringEventChannelSO _onCollectiblePickedUp; // EVT_CollectiblePickup
|
||
[SerializeField] private StringEventChannelSO _onAbilityUnlocked; // EVT_AbilityUnlocked
|
||
[SerializeField] private StringEventChannelSO _onRoomEntered; // EVT_SceneLoaded
|
||
[SerializeField] private StringEventChannelSO _onDialogueCompleted; // EVT_NpcDialogueCompleted
|
||
|
||
// ── 中继 C# 事件,供 ChainCondition.Register() 订阅 ──────────────
|
||
public event Action<string> OnBossDefeated;
|
||
public event Action<string> OnCollectiblePickedUp;
|
||
public event Action<string> OnAbilityUnlocked;
|
||
public event Action<string> OnRoomEntered;
|
||
public event Action<string> OnDialogueCompleted;
|
||
/// <summary>链执行完成时广播 chainId(供 ChainCompletedCondition)。</summary>
|
||
public event Action<string> OnChainCompleted;
|
||
|
||
#if UNITY_EDITOR
|
||
/// <summary>
|
||
/// Editor 专用静态事件:链执行完成时向编辑器窗口推送日志。
|
||
/// 仅在编辑器构建中存在,不产生运行时开销。
|
||
/// EventChainEditorWindow 在 OnEnable 中订阅此事件。
|
||
/// </summary>
|
||
public static event Action<string, string> OnChainExecutedInEditor;
|
||
#endif
|
||
|
||
private readonly HashSet<string> _completedChains = new();
|
||
private readonly CompositeDisposable _subs = new();
|
||
|
||
// ── 生命周期 ──────────────────────────────────────────────────────
|
||
|
||
private void Awake()
|
||
{
|
||
// 从存档恢复已完成链
|
||
var sm = ServiceLocator.GetOrDefault<ISaveService>();
|
||
if (sm != null)
|
||
foreach (var id in sm.GetCompletedChains())
|
||
_completedChains.Add(id);
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
_onBossDefeated?.Subscribe(id => { OnBossDefeated?.Invoke(id); EvaluateAll(); }).AddTo(_subs);
|
||
_onCollectiblePickedUp?.Subscribe(id => { OnCollectiblePickedUp?.Invoke(id); EvaluateAll(); }).AddTo(_subs);
|
||
_onAbilityUnlocked?.Subscribe(id => { OnAbilityUnlocked?.Invoke(id); EvaluateAll(); }).AddTo(_subs);
|
||
_onRoomEntered?.Subscribe(id => { OnRoomEntered?.Invoke(id); EvaluateAll(); }).AddTo(_subs);
|
||
_onDialogueCompleted?.Subscribe(id => { OnDialogueCompleted?.Invoke(id); EvaluateAll(); }).AddTo(_subs);
|
||
|
||
// 向每个 Condition 注册中继事件
|
||
// 先重置 SO 资产上的运行时状态,防止跨 PlayMode 会话或多场景加载时状态残留
|
||
if (_chains == null) return;
|
||
foreach (var chain in _chains)
|
||
if (chain?.conditions != null)
|
||
foreach (var cond in chain.conditions)
|
||
{
|
||
cond?.ResetState();
|
||
cond?.Register(this);
|
||
}
|
||
}
|
||
|
||
private void OnDisable()
|
||
{
|
||
_subs.Clear();
|
||
|
||
if (_chains == null) return;
|
||
foreach (var chain in _chains)
|
||
if (chain?.conditions != null)
|
||
foreach (var cond in chain.conditions)
|
||
cond?.Unregister(this);
|
||
}
|
||
|
||
// ── 评估逻辑 ──────────────────────────────────────────────────────
|
||
/// <summary>收到新事件时立即评估所有链条件(无帧延迟)。</summary>
|
||
private void EvaluateAll() => DoEvaluateAll();
|
||
|
||
private void DoEvaluateAll()
|
||
{
|
||
if (_chains == null) return;
|
||
foreach (var chain in _chains)
|
||
{
|
||
if (chain == null) continue;
|
||
if (!chain.repeatable && _completedChains.Contains(chain.chainId)) continue;
|
||
|
||
bool allMet = true;
|
||
if (chain.conditions != null)
|
||
foreach (var cond in chain.conditions)
|
||
if (cond != null && !cond.IsMet()) { allMet = false; break; }
|
||
|
||
if (allMet) StartCoroutine(ExecuteChain(chain));
|
||
}
|
||
}
|
||
|
||
private IEnumerator ExecuteChain(EventChainSO chain)
|
||
{
|
||
// 防重入:一次性链立即标记为已完成
|
||
if (!chain.repeatable) _completedChains.Add(chain.chainId);
|
||
|
||
if (chain.actions != null)
|
||
foreach (var action in chain.actions)
|
||
{
|
||
if (action == null) continue;
|
||
yield return action.ExecuteAsync(this);
|
||
if (chain.actionDelay > 0f)
|
||
yield return new WaitForSeconds(chain.actionDelay);
|
||
}
|
||
|
||
ServiceLocator.GetOrDefault<ISaveService>()?.SetChainCompleted(chain.chainId);
|
||
OnChainCompleted?.Invoke(chain.chainId);
|
||
#if UNITY_EDITOR
|
||
OnChainExecutedInEditor?.Invoke(chain.chainId, "执行完成");
|
||
#endif
|
||
}
|
||
}
|
||
}
|