using System;
using System.Collections;
using BaseGames.Core;
using BaseGames.Core.Events;
using UnityEngine;
namespace BaseGames.EventChain
{
// =====================================================================
// EventChainSO —— 世界事件链数据(架构 14_NarrativeModule §9)
// =====================================================================
///
/// 世界事件链:描述"当全部 conditions 满足时,依序执行 actions"。
/// 策划纯数据配置,无需程序员介入。
/// 资产路径:Assets/ScriptableObjects/EventChains/Chain_{Context}.asset
///
[CreateAssetMenu(menuName = "BaseGames/EventChain/EventChain")]
public class EventChainSO : ScriptableObject
{
[Header("基础")]
public string chainId; // 全局唯一,如 "Chain_BossForest_Defeated"
public bool repeatable; // false = 只触发一次(触发后存档记录)
[Min(0f)]
public float actionDelay = 0f; // 各 action 之间的延迟(秒),0 = 紧接着执行
[Header("触发条件(全部满足才触发)")]
public ChainCondition[] conditions;
[Header("执行动作(顺序执行)")]
public ChainAction[] actions;
}
// =====================================================================
// ChainCondition 抽象基类 + 内置实现
// =====================================================================
///
/// 事件链触发条件抽象基类(Strategy + Observer 混合)。
/// Register/Unregister 向 EventChainManager 的中继 C# 事件挂钩,
/// IsMet() 被 EvaluateAll() 调用以检验是否满足触发条件。
///
public abstract class ChainCondition : ScriptableObject
{
public abstract void Register(EventChainManager manager);
public abstract void Unregister(EventChainManager manager);
public abstract bool IsMet();
///
/// 重置运行时瞬态状态(每次 EventChainManager.OnEnable 时调用)。
/// ScriptableObject 是资产,_met 等字段会跨 PlayMode 会话残留;
/// 显式重置确保每次进入游戏/切换场景时条件均从初始状态开始评估。
///
public virtual void ResetState() { }
}
// ─── 内置条件 ──────────────────────────────────────────────────────────
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/BossDefeated")]
public class BossDefeatedCondition : ChainCondition
{
public string bossId;
private bool _met;
public override void Register(EventChainManager m) => m.OnBossDefeated += Check;
public override void Unregister(EventChainManager m) => m.OnBossDefeated -= Check;
public override bool IsMet() => _met;
public override void ResetState() => _met = false;
private void Check(string id) { if (id == bossId) _met = true; }
}
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/FlagSet")]
public class FlagSetCondition : ChainCondition
{
public string flagId;
public override void Register(EventChainManager m) { } // 持续轮询,无需订阅
public override void Unregister(EventChainManager m) { }
public override bool IsMet() { var sm = ServiceLocator.GetOrDefault(); return sm != null && sm.GetFlag(flagId); }
}
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/AbilityUnlocked")]
public class AbilityUnlockedCondition : ChainCondition
{
public string abilityId;
private bool _met;
public override void Register(EventChainManager m) => m.OnAbilityUnlocked += Check;
public override void Unregister(EventChainManager m) => m.OnAbilityUnlocked -= Check;
public override bool IsMet() => _met;
public override void ResetState() => _met = false;
private void Check(string id) { if (id == abilityId) _met = true; }
}
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/CollectibleCollected")]
public class CollectibleCollectedCondition : ChainCondition
{
public string itemId;
private bool _met;
public override void Register(EventChainManager m) => m.OnCollectiblePickedUp += Check;
public override void Unregister(EventChainManager m) => m.OnCollectiblePickedUp -= Check;
public override bool IsMet() => _met;
public override void ResetState() => _met = false;
private void Check(string id) { if (id == itemId) _met = true; }
}
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/RoomEntered")]
public class RoomEnteredCondition : ChainCondition
{
public string sceneName;
private bool _met;
public override void Register(EventChainManager m) => m.OnRoomEntered += Check;
public override void Unregister(EventChainManager m) => m.OnRoomEntered -= Check;
public override bool IsMet() => _met;
public override void ResetState() => _met = false;
private void Check(string id) { if (id == sceneName) _met = true; }
}
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/DialogueCompleted")]
public class DialogueCompletedCondition : ChainCondition
{
public string npcId;
private bool _met;
public override void Register(EventChainManager m) => m.OnDialogueCompleted += Check;
public override void Unregister(EventChainManager m) => m.OnDialogueCompleted -= Check;
public override bool IsMet() => _met;
public override void ResetState() => _met = false;
private void Check(string id) { if (id == npcId) _met = true; }
}
[CreateAssetMenu(menuName = "BaseGames/EventChain/Condition/ChainCompleted")]
public class ChainCompletedCondition : ChainCondition
{
public string chainId;
private bool _met;
public override void Register(EventChainManager m) => m.OnChainCompleted += Check;
public override void Unregister(EventChainManager m) => m.OnChainCompleted -= Check;
public override bool IsMet() => _met;
public override void ResetState() => _met = false;
private void Check(string id) { if (id == chainId) _met = true; }
}
// =====================================================================
// ChainAction 抽象基类 + 内置实现
// =====================================================================
///
/// 事件链执行动作抽象基类。ExecuteAsync 可即时返回或协程等待。
///
public abstract class ChainAction : ScriptableObject
{
public abstract IEnumerator ExecuteAsync(MonoBehaviour runner);
}
// ─── 内置动作 ──────────────────────────────────────────────────────────
/// 打开门(发布 EVT_DoorOpened 事件)。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/OpenDoor")]
public class OpenDoorAction : ChainAction
{
public string doorId;
[SerializeField] private StringEventChannelSO _onDoorOpened; // EVT_DoorOpened
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
_onDoorOpened?.Raise(doorId);
yield break;
}
}
/// 设置/清除存档标志。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/SetFlag")]
public class SetFlagAction : ChainAction
{
public string flagId;
public bool value = true;
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
ServiceLocator.GetOrDefault()?.SetFlag(flagId, value);
yield break;
}
}
/// 通知地图显示/标记区域(通过事件频道解耦,无直接 MapManager 依赖)。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/UpdateMap")]
public class UpdateMapAction : ChainAction
{
public string regionId;
[SerializeField] private StringEventChannelSO _onRevealRegion; // EVT_RevealRegion → MapManager 订阅
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
_onRevealRegion?.Raise(regionId);
yield break;
}
}
/// 播放过场动画,等待其结束再继续链。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/PlayCutscene")]
public class PlayCutsceneAction : ChainAction
{
public string cutsceneId;
[SerializeField] private StringEventChannelSO _onPlayCutscene; // → CutsceneManager.PlayById
[SerializeField] private VoidEventChannelSO _onCutsceneEnded; // ← CutsceneManager 播完后 Raise
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
bool done = false;
var sub = _onCutsceneEnded != null
? _onCutsceneEnded.Subscribe(() => done = true)
: default(EventSubscription);
_onPlayCutscene?.Raise(cutsceneId);
yield return new WaitUntil(() => done);
sub.Dispose();
}
}
/// 切换 NPC 对话(通过 EVT_NPCDialogueChange 广播,NPC 自行响应)。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/ChangeNPCDialogue")]
public class ChangeNPCDialogueAction : ChainAction
{
public string npcId;
public string newSequenceId;
[SerializeField] private StringEventChannelSO _onNPCDialogueChange; // EVT_NPCDialogueChange → NPC 订阅
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
_onNPCDialogueChange?.Raise($"{npcId}:{newSequenceId}");
yield break;
}
}
/// 在场景中生成一个预制体。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/SpawnObject")]
public class SpawnObjectAction : ChainAction
{
public GameObject prefab;
public Vector3 position;
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
if (prefab != null)
UnityEngine.Object.Instantiate(prefab, position, Quaternion.identity);
yield break;
}
}
/// 等待指定秒数。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/Wait")]
public class WaitAction : ChainAction
{
[Min(0f)]
public float seconds;
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
yield return new WaitForSeconds(seconds);
}
}
/// 发布一个无负载 Void 事件频道。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/RaiseEvent")]
public class RaiseEventAction : ChainAction
{
[SerializeField] private VoidEventChannelSO _eventChannel;
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
_eventChannel?.Raise();
yield break;
}
}
/// 解锁能力(通过 EVT_AbilityUnlocked 事件广播,PlayerStats 响应)。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/UnlockAbility")]
public class UnlockAbilityAction : ChainAction
{
public string abilityId;
[SerializeField] private StringEventChannelSO _onAbilityUnlock; // EVT_AbilityUnlocked
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
_onAbilityUnlock?.Raise(abilityId);
yield break;
}
}
/// 切换背景音乐(通过 EVT_PlayBGM 事件广播)。
[CreateAssetMenu(menuName = "BaseGames/EventChain/Action/PlayAudio")]
public class PlayAudioAction : ChainAction
{
public string bgmKey;
[SerializeField] private StringEventChannelSO _onPlayBGM; // EVT_PlayBGM
public override IEnumerator ExecuteAsync(MonoBehaviour runner)
{
_onPlayBGM?.Raise(bgmKey);
yield break;
}
}
}