using System.Collections.Generic; using UnityEngine; using BaseGames.Core.Events; using BaseGames.Core.Save; using QuestStateEnum = BaseGames.Core.Events.QuestState; namespace BaseGames.Quest { /// /// 运行时任务管理器(架构 22_QuestChallengeModule §5)。 /// 挂在 Persistent 场景 [GameManagers] 下。 /// 事件驱动追踪目标进度,不主动轮询。 /// 实现 ISaveable,通过 SaveManager 持久化任务状态。 /// public class QuestManager : MonoBehaviour, ISaveable, IQuestManager { // ── Inspector ──────────────────────────────────────────────────────── [SerializeField] private QuestSO[] _allQuests; [Header("Event Channels(监听)")] [SerializeField] private StringEventChannelSO _onEnemyDied; // EVT_EnemyDied(enemyId) [SerializeField] private StringEventChannelSO _onCollectiblePickup; // EVT_CollectiblePickup(itemId) [SerializeField] private StringEventChannelSO _onSceneLoaded; // EVT_SceneLoaded(sceneName) [SerializeField] private StringEventChannelSO _onNpcDialogueCompleted; // EVT_NpcDialogueCompleted(npcId) [Header("Event Channels(广播)")] [SerializeField] private StringEventChannelSO _onQuestStarted; // questId [SerializeField] private StringEventChannelSO _onQuestCompleted; // questId [SerializeField] private StringEventChannelSO _onQuestFailed; // questId [SerializeField] private QuestObjectiveEventChannelSO _onObjectiveUpdated; // ── Runtime State ──────────────────────────────────────────────────── private readonly Dictionary _questStates = new(); private readonly Dictionary _objectiveStates = new(); /// questId → QuestSO 快速查找表(由 Awake 构建,将 GetQuestSO O(n) 降为 O(1))。 private Dictionary _questIndex; private readonly CompositeDisposable _subs = new(); public StringEventChannelSO OnQuestStarted => _onQuestStarted; public StringEventChannelSO OnQuestCompleted => _onQuestCompleted; /// 供 SaveManager 迭代的任务状态字典(只读视图)。 public IReadOnlyDictionary QuestStates => _questStates; private void Awake() { if (BaseGames.Core.ServiceLocator.GetOrDefault() != null) { Destroy(gameObject); return; } BaseGames.Core.ServiceLocator.Register(this); // 构建任务字典索引,将 GetQuestSO 变为 O(1) _questIndex = new Dictionary(_allQuests?.Length ?? 0); if (_allQuests != null) foreach (var q in _allQuests) if (q != null && !string.IsNullOrEmpty(q.questId)) _questIndex[q.questId] = q; } private void OnEnable() { _onEnemyDied?.Subscribe(HandleEnemyDefeated).AddTo(_subs); _onCollectiblePickup?.Subscribe(HandleItemCollected).AddTo(_subs); _onSceneLoaded?.Subscribe(HandleSceneLoaded).AddTo(_subs); _onNpcDialogueCompleted?.Subscribe(HandleNpcDialogue).AddTo(_subs); BaseGames.Core.ServiceLocator.GetOrDefault()?.Register(this); } private void OnDisable() { _subs.Clear(); BaseGames.Core.ServiceLocator.GetOrDefault()?.Unregister(this); } private void OnDestroy() { BaseGames.Core.ServiceLocator.Unregister(this); } // ── 公共 API ────────────────────────────────────────────────────────── /// NPC 接受任务时调用。 public void AcceptQuest(string questId) { if (!CanAccept(questId)) return; _questStates[questId] = QuestStateEnum.Active; _onQuestStarted?.Raise(questId); } /// NPC 完成任务时调用。 public void CompleteQuest(string questId, IRewardTarget rewardTarget) { if (!IsReadyToComplete(questId)) return; var quest = GetQuestSO(questId); quest.reward?.Apply(rewardTarget); _questStates[questId] = QuestStateEnum.Completed; _onQuestCompleted?.Raise(questId); // 解锁后续任务(分支) if (quest.branches != null) { foreach (var branch in quest.branches) { if (string.IsNullOrEmpty(branch.conditionQuestId) || GetState(branch.conditionQuestId) == QuestStateEnum.Completed) { if (branch.nextQuest != null) _questStates[branch.nextQuest.questId] = QuestStateEnum.Available; break; } } } } public QuestStateEnum GetState(string questId) => _questStates.TryGetValue(questId, out var s) ? s : QuestStateEnum.Unavailable; public bool IsReadyToComplete(string questId) { var quest = GetQuestSO(questId); if (quest == null || GetState(questId) != QuestStateEnum.Active) return false; if (quest.objectives == null) return true; foreach (var obj in quest.objectives) { if (!obj.IsOptional && !IsObjectiveComplete(obj)) return false; } return true; } // ── ISaveable ───────────────────────────────────────────────────────── public void OnSave(SaveData data) { data.Quests.QuestStates.Clear(); foreach (var (id, state) in _questStates) { data.Quests.QuestStates[id] = new BaseGames.Core.Save.QuestState { Status = state.ToString(), ObjectiveIndex = 0, ProgressCounts = BuildProgressList(id), }; } } public void OnLoad(SaveData data) { _questStates.Clear(); _objectiveStates.Clear(); foreach (var (id, saved) in data.Quests.QuestStates) { if (System.Enum.TryParse(saved.Status, out var parsedState)) _questStates[id] = parsedState; // 恢复各目标进度 var quest = GetQuestSO(id); if (quest?.objectives != null && saved.ProgressCounts != null) { for (int i = 0; i < quest.objectives.Length && i < saved.ProgressCounts.Count; i++) { var obj = quest.objectives[i]; if (obj == null) continue; if (!_objectiveStates.TryGetValue(obj.objectiveId, out var os)) os = _objectiveStates[obj.objectiveId] = new QuestObjectiveState(); os.progressCount = saved.ProgressCounts[i]; } } } } // ── 私有辅助 ───────────────────────────────────────────────────────── private bool CanAccept(string questId) { if (GetState(questId) != QuestStateEnum.Available) return false; var quest = GetQuestSO(questId); if (quest?.prerequisiteQuestIds == null) return true; foreach (var pre in quest.prerequisiteQuestIds) if (GetState(pre) != QuestStateEnum.Completed) return false; return true; } private bool IsObjectiveComplete(QuestObjectiveSO obj) { _objectiveStates.TryGetValue(obj.objectiveId, out var s); s ??= new QuestObjectiveState(); return obj.EvaluateCompletion(s); } private List BuildProgressList(string questId) { var list = new List(); var quest = GetQuestSO(questId); if (quest?.objectives == null) return list; foreach (var obj in quest.objectives) { _objectiveStates.TryGetValue(obj?.objectiveId ?? string.Empty, out var os); list.Add(os?.progressCount ?? 0); } return list; } // ── 事件处理 ───────────────────────────────────────────────────────── private void HandleEnemyDefeated(string enemyId) { ForEachActiveObjective(obj => { if (obj.targetEnemyId == enemyId) IncrementProgress(obj.objectiveId); }); } private void HandleItemCollected(string itemId) { ForEachActiveObjective(obj => { if (obj.itemId == itemId) IncrementProgress(obj.objectiveId); }); } private void HandleNpcDialogue(string npcId) { ForEachActiveObjective(obj => { if (obj.targetNpcId == npcId) IncrementProgress(obj.objectiveId); }); } private void HandleSceneLoaded(string sceneName) { ForEachActiveObjective(obj => { if (obj.sceneName == sceneName) IncrementProgress(obj.objectiveId); }); } private void ForEachActiveObjective(System.Action action) where T : QuestObjectiveSO { foreach (var (qid, state) in _questStates) { if (state != QuestStateEnum.Active) continue; var quest = GetQuestSO(qid); if (quest?.objectives == null) continue; foreach (var obj in quest.objectives) { if (obj is T typed) action(typed); } } } private void IncrementProgress(string objectiveId) { if (!_objectiveStates.TryGetValue(objectiveId, out var s)) s = _objectiveStates[objectiveId] = new QuestObjectiveState(); s.progressCount++; _onObjectiveUpdated?.Raise(new QuestObjectiveEvent { ObjectiveId = objectiveId, Progress = s.progressCount, }); } private QuestSO GetQuestSO(string id) => _questIndex != null && _questIndex.TryGetValue(id, out var q) ? q : null; } }