using System;
using BaseGames.Core;
using BaseGames.World;
using UnityEngine;
namespace BaseGames.Dialogue
{
///
/// 条件对话 NPC(架构 14_NarrativeModule §7)。
/// 扩展 InteractableNPC,根据世界状态标志动态选择对话版本。
/// 版本列表从高到低优先级排列;第一个满足条件的版本生效。
///
/// _worldState 可留空:留空时自动从 ServiceLocator 获取全局注册的 IWorldStateReader,
/// 便于无需在每个 NPC Prefab 上手动拖入 WorldStateRegistry 资产。
///
public class NarrativeNPC : InteractableNPC
{
[Header("台词版本集(从高到低优先级排列)")]
[Tooltip("条件对话版本列表。运行时从上到下检查,第一个满足条件的版本被播放。\n" +
"版本之间的优先级由列表顺序决定——请将最具体的条件放在最上方。")]
[SerializeField] private DialogueVersion[] _dialogueVersions;
[Tooltip("所有版本均不满足条件时的兜底对话。务必配置,否则运行时会输出 LogWarning 且 NPC 无对话。")]
[SerializeField] private DialogueSequenceSO _fallbackDialogue;
[Tooltip("世界状态 SO(可选)。留空时自动从 ServiceLocator 获取全局 IWorldStateReader。\n" +
"通常同场景下多个 NPC 共用同一个 WorldStateRegistry;\n" +
"若全局已通过 ServiceLocator 注册,可不在此处手动指定。")]
[SerializeField] private WorldStateRegistry _worldState;
protected override DialogueSequenceSO GetCurrentDialogue()
{
IWorldStateReader reader = _worldState
?? ServiceLocator.GetOrDefault();
if (_dialogueVersions == null) return _fallbackDialogue;
foreach (var version in _dialogueVersions)
{
if (version != null && version.CheckConditions(reader))
return version.dialogue;
}
if (_fallbackDialogue == null)
Debug.LogWarning($"[NarrativeNPC] '{name}' 没有版本满足当前条件,且未配置兜底对话 (_fallbackDialogue)。", gameObject);
return _fallbackDialogue;
}
}
// ─────────────────────────────────────────────────────────────────────────
///
/// 一个对话版本及其激活条件(架构 14_NarrativeModule §7)。
///
[Serializable]
public class DialogueVersion
{
[Tooltip("编辑器显示名,如'森林 Boss 击败后'")]
public string versionLabel;
[Tooltip("此版本对应的对话序列 SO。条件满足时播放。留空时等同于跳过此版本。")]
public DialogueSequenceSO dialogue;
[Tooltip("全部满足才激活此版本(AND 关系)")]
[WorldStateFlag]
public string[] requiredFlags;
[Tooltip("有任意一个 = 此版本不激活(NOT 关系)")]
[WorldStateFlag]
public string[] blockedByFlags;
///
/// 检查此版本的激活条件(AND requiredFlags / NOT blockedByFlags)。
/// reader 为 null 时直接返回 false(无法判断,视为条件不满足)。
///
public bool CheckConditions(IWorldStateReader reader)
{
if (reader == null) return false;
if (requiredFlags != null)
foreach (var f in requiredFlags)
if (!reader.HasFlag(f)) return false;
if (blockedByFlags != null)
foreach (var f in blockedByFlags)
if (reader.HasFlag(f)) return false;
return true;
}
}
}