using UnityEngine; using BaseGames.Core; using BaseGames.Dialogue; using BaseGames.Player; using QuestStateEnum = BaseGames.Core.Events.QuestState; using SL = BaseGames.Core.ServiceLocator; using L10n = BaseGames.Localization.LocalizationManager; namespace BaseGames.Quest { /// /// 可发布/完成任务的 NPC(架构 22_QuestChallengeModule §6)。 /// 继承 InteractableNPC,根据任务状态切换对话版本,在交互时处理任务接收/完成。 /// public class QuestGiver : InteractableNPC { [Header("任务")] [Tooltip("该 NPC 可提供的所有任务,按优先级从高到低排列。\n" + "交互时从列表头部找到第一个 Available 或 Active 状态的任务作为当前任务;\n" + "若全部已完成,显示最后一个已完成任务的 completedDialogue。")] [SerializeField] private QuestSO[] _offeredQuests; [Header("对话版本(根据任务状态切换)")] [Tooltip("任务尚未接取(QuestState.Available)时播放。通常是 NPC 发布任务、介绍背景的对话。")] [SerializeField] private DialogueSequenceSO _availableDialogue; [Tooltip("任务已接取、目标尚未全部完成(QuestState.Active)时播放。通常是 NPC 催促或加油打气的对话。")] [SerializeField] private DialogueSequenceSO _activeDialogue; [Tooltip("全部非可选目标已达成、任务可以交付时播放(IsReadyToComplete = true)。\n" + "通常是 NPC 感谢、确认收取物品的对话,播放后执行 CompleteQuest 逻辑。")] [SerializeField] private DialogueSequenceSO _readyDialogue; [Tooltip("任务已完成(QuestState.Completed)后再次交互时播放。通常是 NPC 闲聊或后续剧情的对话。")] [SerializeField] private DialogueSequenceSO _completedDialogue; // ── InteractableNPC 覆盖 ────────────────────────────────────────────── // 缓存上次查找结果,避免 InteractPrompt get(每帧调用)重复遍历 _offeredQuests。 // 当状态可能变更时(OnEnable、Interact_Internal 后)标记为脏。 private QuestSO _cachedQuest; private QuestStateEnum _cachedState; private bool _cacheDirty = true; // 本地化 Key 常量 — 对应 UI 本地化表中的条目; // 如本地化表未配置该 Key,GetPrompt 会降级为内联的中文默认文本。 private const string K_Accept = "QUEST_PROMPT_ACCEPT"; private const string K_Submit = "QUEST_PROMPT_SUBMIT"; private const string K_InProgress = "QUEST_PROMPT_IN_PROGRESS"; private const string K_Paused = "QUEST_PROMPT_PAUSED"; private const string K_Talk = "QUEST_PROMPT_TALK"; // 缓存 IQuestManager + IQuestEventSource 引用,避免每次访问 InteractPrompt 调用 SL.GetOrDefault private IQuestManager _questManager; private IQuestEventSource _questEvents; protected override void OnEnable() { base.OnEnable(); _cacheDirty = true; _questManager = SL.GetOrDefault(); _questEvents = _questManager as IQuestEventSource; if (_questEvents != null) { _questEvents.OnQuestStateChanged += HandleQuestStateChanged; _questEvents.OnAfterSaveLoaded += HandleAfterSaveLoaded; } } protected override void OnDisable() { base.OnDisable(); if (_questEvents != null) { _questEvents.OnQuestStateChanged -= HandleQuestStateChanged; _questEvents.OnAfterSaveLoaded -= HandleAfterSaveLoaded; _questEvents = null; } _questManager = null; } // 任务状态变化时自动标记缓存失效(无需再手动设 _cacheDirty) private void HandleQuestStateChanged(string questId, QuestStateEnum from, QuestStateEnum to) { if (_offeredQuests == null) return; foreach (var q in _offeredQuests) if (q != null && q.questId == questId) { _cacheDirty = true; return; } } // 存档加载完成后统一刷新缓存,确保 NPC 交互提示反映最新任务状态 private void HandleAfterSaveLoaded() => _cacheDirty = true; // 本地化提示词辅助:如 Key 在表中找不到(返回值等于 Key 自身),回退到内联默认文本 private static string GetPrompt(string key, string fallback) { var v = L10n.Get(key, "UI"); return v != key ? v : fallback; } public override string InteractPrompt { get { var quest = GetCachedQuest(); if (quest == null || _questManager == null) return base.InteractPrompt; return _cachedState switch { QuestStateEnum.Available => GetPrompt(K_Accept, "接受任务"), QuestStateEnum.Active => _questManager.IsReadyToComplete(quest.questId) ? GetPrompt(K_Submit, "提交任务") : GetPrompt(K_InProgress, "进行中…"), QuestStateEnum.Paused => GetPrompt(K_Paused, "暂停中…"), QuestStateEnum.Completed => GetPrompt(K_Talk, "对话"), _ => base.InteractPrompt, }; } } protected override void Interact_Internal(Transform player) { var quest = GetCachedQuest(); if (quest == null || _questManager == null) return; if (_cachedState == QuestStateEnum.Available) { _questManager.AcceptQuest(quest.questId); // OnQuestStateChanged 事件会自动触发 HandleQuestStateChanged → _cacheDirty = true } else if (_cachedState == QuestStateEnum.Active && _questManager.IsReadyToComplete(quest.questId)) { // 直接从 player 获取 PlayerStats,避免对 PlayerController 的程序集依赖 var stats = player.GetComponentInParent(); _questManager.CompleteQuest(quest.questId, stats); // OnQuestStateChanged 事件会自动触发 HandleQuestStateChanged → _cacheDirty = true } } protected override DialogueSequenceSO GetCurrentDialogue() { var quest = GetCachedQuest(); if (quest == null || _questManager == null) return base.GetCurrentDialogue(); return _cachedState switch { QuestStateEnum.Available => _availableDialogue, QuestStateEnum.Active => _questManager.IsReadyToComplete(quest.questId) ? _readyDialogue : _activeDialogue, QuestStateEnum.Paused => _activeDialogue, // 暂停中显示"催促"对话,不触发任何状态推进 QuestStateEnum.Completed => _completedDialogue, _ => base.GetCurrentDialogue(), }; } // ── 私有辅助 ───────────────────────────────────────────────────────── /// /// 返回缓存的当前任务(处于 Available/Active/Paused 的第一个,或最后一个已完成任务)。 /// 若缓存不脏,直接返回上次结果,避免每帧遍历 _offeredQuests。 /// 任务状态事件(HandleQuestStateChanged)或 OnEnable 会自动将 _cacheDirty 置 true, /// 确保下次访问状态是最新的。 /// private QuestSO GetCachedQuest() { if (!_cacheDirty && _cachedQuest != null) return _cachedQuest; var qm = _questManager; if (_offeredQuests == null || qm == null) { _cacheDirty = false; return null; } QuestSO lastCompleted = null; foreach (var q in _offeredQuests) { if (q == null) continue; var s = qm.GetState(q.questId); if (s == QuestStateEnum.Available || s == QuestStateEnum.Active || s == QuestStateEnum.Paused) { _cachedQuest = q; _cachedState = s; _cacheDirty = false; return _cachedQuest; } if (s == QuestStateEnum.Completed) lastCompleted = q; } _cachedQuest = lastCompleted; _cachedState = lastCompleted != null ? QuestStateEnum.Completed : QuestStateEnum.Unavailable; _cacheDirty = false; return _cachedQuest; } } }