Files
zeling_v2/Assets/_Game/Scripts/Quest/IQuestManager.cs
Joywayer 3c3ea1ead6 feat: Round 49 narrative systems improvements
QuestManager: extract CheckQuestDepsAndFlags shared method, simplify GetQuestLockInfo/CanAccept/MeetsPrerequisites; add GetQuestsInState+FilterQuests implementations; fix extra brace compile bug; add _pauseTimestamps logging; use actualDelta in ApplyAffinity event.

QuestSO: add depth>32 guard to HasPrerequisiteCycle and HasBranchCycle to prevent editor freeze on deep chains.

EventChainModule: replace FindObjectOfType with ServiceLocator.GetOrDefault in ForceExecute; add self-trigger flag detection (check 6) in ValidateAllChains using reflection.

DialogueVariantPreviewWindow: add matrix analysis section enumerating all 2^N flag combinations (N<=10) with table showing winning variant per combination.

WorldStateRegistry: LoadFromSave null guard on data.World sub-collections (P0 fix).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-25 00:17:27 +08:00

185 lines
9.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using BaseGames.Core.Events;
using QuestStateEnum = BaseGames.Core.Events.QuestState;
namespace BaseGames.Quest
{
// =========================================================================
// QuestLockReason / QuestLockInfo ── 任务锁定原因(强类型 API
// =========================================================================
/// <summary>任务无法接取的原因枚举。<see cref="None"/> 表示无锁定(可接取)。</summary>
public enum QuestLockReason
{
/// <summary>无锁定,任务当前可以接取。</summary>
None,
/// <summary>任务已在进行中Active。</summary>
AlreadyActive,
/// <summary>任务已完成Completed。</summary>
AlreadyCompleted,
/// <summary>任务已失败Failed。</summary>
Failed,
/// <summary>任务已暂停Paused。</summary>
Paused,
/// <summary>任务 ID 未找到或资产未加载。</summary>
NotFound,
/// <summary>好感度或存档数据尚未初始化。</summary>
DataNotLoaded,
/// <summary>NPC 好感度不足。<see cref="QuestLockInfo.Param"/> 格式:"{actual}/{min}"。</summary>
InsufficientAffinity,
/// <summary>前置任务未完成。<see cref="QuestLockInfo.Param"/> 为该前置任务的 questId。</summary>
RequiresQuest,
/// <summary>世界状态标志条件未满足。</summary>
FlagConditionNotMet,
}
/// <summary>
/// 任务锁定信息(强类型版本)。
/// 相比字符串 Key可在编译期检查原因类型UI 层无需手动解析冒号分隔的参数。
/// 通过 <see cref="ToLocalizationKey"/> 可转换为与旧版 <c>GetQuestLockReason</c> 兼容的 Key 格式。
/// </summary>
public struct QuestLockInfo
{
/// <summary>锁定原因枚举值。<see cref="QuestLockReason.None"/> 表示无锁定(可接取)。</summary>
public QuestLockReason Reason;
/// <summary>
/// 附带参数(可选):<br/>
/// - <see cref="QuestLockReason.RequiresQuest"/>:前置任务 questId<br/>
/// - <see cref="QuestLockReason.InsufficientAffinity"/>:格式 "{actual}/{min}"
/// </summary>
public string Param;
/// <summary>任务当前是否处于锁定状态(不可接取)。</summary>
public bool IsLocked => Reason != QuestLockReason.None;
/// <summary>
/// 转换为本地化 Key 格式,与旧版 <see cref="IQuestManager.GetQuestLockReason"/> 完全兼容。
/// 格式:<c>"Quest.LockReason.{Reason}"</c>;有参数时为 <c>"Quest.LockReason.{Reason}:{Param}"</c>。
/// </summary>
public string ToLocalizationKey() =>
Reason == QuestLockReason.None
? string.Empty
: string.IsNullOrEmpty(Param)
? $"Quest.LockReason.{Reason}"
: $"Quest.LockReason.{Reason}:{Param}";
}
/// <summary>
/// 任务管理器的公开契约。ServiceLocator.Get&lt;IQuestManager&gt;() 获取实例,
/// 避免外部代码直接依赖 QuestManager 具体类型。
/// </summary>
public interface IQuestManager
{
/// <summary>接取任务(幂等)。</summary>
void AcceptQuest(string questId);
/// <summary>
/// 主动放弃进行中的任务Active → Available/Unavailable清除目标进度。
/// 非 Active 状态的任务调用此方法无效。
/// </summary>
void AbandonQuest(string questId);
/// <summary>完成任务并发放奖励。rewardTarget 接收奖励(如玩家)。</summary>
void CompleteQuest(string questId, IRewardTarget rewardTarget);
/// <summary>
/// 暂停进行中的任务Active → Paused。暂停期间目标不推进失败条件不判定。
/// 非 Active 状态的任务调用此方法无效。
/// </summary>
void PauseQuest(string questId);
/// <summary>
/// 恢复已暂停的任务Paused → Active
/// 非 Paused 状态的任务调用此方法无效。
/// </summary>
void ResumeQuest(string questId);
/// <summary>返回当前任务状态。未知 questId 返回 Unavailable。</summary>
QuestStateEnum GetState(string questId);
/// <summary>判断任务是否满足完成条件。</summary>
bool IsReadyToComplete(string questId);
/// <summary>返回指定 NPC 的当前好感度数值(未记录时返回 0。</summary>
int GetNpcAffinity(string npcId);
/// <summary>
/// 返回任务无法被接取的原因(本地化 Key 格式)。
/// 若任务当前可以接取,返回空字符串。
/// Key 格式:<c>"Quest.LockReason.{Reason}"</c>;带动态参数时以冒号分隔,如
/// <c>"Quest.LockReason.RequiresQuest:Quest_FindMushroom"</c>。
/// <para>推荐新代码使用 <see cref="GetQuestLockInfo"/> 获取强类型结果,无需手动解析字符串。</para>
/// </summary>
string GetQuestLockReason(string questId);
/// <summary>
/// 返回任务无法被接取的强类型锁定信息。
/// 相比 <see cref="GetQuestLockReason"/>可在编译期检查原因枚举UI 层无需解析字符串。
/// 若任务当前可以接取,返回 <see cref="QuestLockInfo.Reason"/> 为 <see cref="QuestLockReason.None"/> 的实例。
/// </summary>
QuestLockInfo GetQuestLockInfo(string questId);
/// <summary>
/// 返回当前处于指定状态的所有任务 ID 快照列表。
/// 适用于任务日志 UI 分组显示、成就系统批量统计等场景。
/// </summary>
System.Collections.Generic.IReadOnlyList<string> GetQuestsInState(QuestStateEnum state);
/// <summary>
/// 对所有已注册任务执行谓词过滤,返回满足条件的任务 ID 快照列表。
/// 适用于自定义筛选(如"活跃且含 NPC 亲密度门槛")等场景。
/// </summary>
System.Collections.Generic.IReadOnlyList<string> FilterQuests(System.Func<string, QuestStateEnum, bool> predicate);
}
/// <summary>
/// 任务事件订阅接口。
/// 外部系统成就、地图标记、HUD、埋点通过此接口订阅任务生命周期事件
/// 无需直接持有 StringEventChannelSO保持与 QuestManager 具体实现的解耦。
/// 获取方式:<c>ServiceLocator.Get&lt;IQuestManager&gt;() as IQuestEventSource</c>
/// </summary>
public interface IQuestEventSource
{
/// <summary>任务成功接取时触发。参数 = questId。</summary>
event Action<string> OnQuestStarted;
/// <summary>任务完成时触发。参数 = questId。</summary>
event Action<string> OnQuestCompleted;
/// <summary>任务失败时触发。参数 = questId。</summary>
event Action<string> OnQuestFailed;
/// <summary>任务被主动放弃时触发。参数 = questId。</summary>
event Action<string> OnQuestAbandoned;
/// <summary>任务暂停时触发Active → Paused。参数 = questId。供埋点/分析系统使用。</summary>
event Action<string> OnQuestPaused;
/// <summary>任务从暂停恢复时触发Paused → Active。参数 = questId。供埋点/分析系统使用。</summary>
event Action<string> OnQuestResumed;
/// <summary>目标全部达成、可回去交任务时触发(去重,同任务只触发一次)。参数 = questId。</summary>
event Action<string> OnQuestReadyToComplete;
/// <summary>
/// 任务状态发生任意转换时触发(涵盖所有状态变更,含旧状态和新状态)。
/// 供状态机审计面板、通用 UI 绑定(无需分别订阅六个离散事件)使用。
/// 参数:(questId, oldState, newState)。
/// </summary>
event Action<string, QuestStateEnum, QuestStateEnum> OnQuestStateChanged;
}
#if UNITY_EDITOR || DEVELOPMENT_BUILD
/// <summary>
/// 任务调试接口(仅编辑器 / 开发构建可用)。
/// 通过 <c>(IQuestManager as IQuestDebugger)?.ResetQuest(id)</c> 使用,
/// 正式发布构建中此接口不存在,调用方无需任何 #if 守卫。
/// </summary>
public interface IQuestDebugger
{
/// <summary>
/// 将任务重置为 Available前置满足或 Unavailable前置未满足并清除目标进度。
/// 不广播 QuestStarted / QuestCompleted 等运行时事件,仅用于开发/调试。
/// </summary>
/// <param name="questId">要重置的任务 ID。</param>
/// <param name="rollbackAffinity">若为 true默认同步回滚此任务对应 NPC 的好感度增量,
/// 防止调试期间重复完成导致好感度叠加。</param>
void ResetQuest(string questId, bool rollbackAffinity = true);
}
#endif
}