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();
data.Quests.AvailableQuestIds.Clear();
foreach (var (id, state) in _questStates)
{
data.Quests.QuestStates[id] = new BaseGames.Core.Save.QuestState
{
Status = state.ToString(),
ObjectiveIndex = 0,
ProgressCounts = BuildProgressList(id),
};
if (state == QuestStateEnum.Available) data.Quests.AvailableQuestIds.Add(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;
}
}