fix: Round 56 亲密度门槛UI、空npcId警告、任务总览窗口、超时缓存、本地化Key检查、放弃任务交互、CurrentNpcId属性

- QuestManager.ApplyAffinity: giverNpc.npcId 为空时改为 LogWarning+return,不再静默丢弃好感度奖励
- QuestManager.UnlockBranches: 分支对话 npcId 为空时输出 LogWarning,提示开发者可能误推进对话类目标
- QuestGiver.InteractPrompt: Available 状态调用 GetQuestLockInfo,亲密度/前置未满足时显示锁定原因而非'接受任务'
- QuestGiver.Interact_Internal: Available 状态加锁定检查防卫,锁定时提前返回;新增 _allowAbandon 字段(默认 false)
- QuestGiver: Active+未完成+_allowAbandon=true 时显示'放弃任务'并触发 AbandonQuest,接入已有 AbandonQuest 接口
- DialogueManager: 新增 _waitSequenceTimeout 缓存字段,Awake 预创建避免每次 PlayImmediate 分配 WaitForSeconds
- DialogueManager: 新增 _currentNpcId 字段,PlayImmediate 写入、EndDialogue/ForceEnd 清空
- IDialogueService + DialogueManager: 暴露 CurrentNpcId 只读属性,供外部系统主动查询当前对话 NPC
- QuestSO.OnValidate: 对空 displayNameKey/descriptionKey 输出 LogWarning,防止 UI 显示空文本
- 新增 QuestOverviewEditorWindow: BaseGames/Quest/Quest Overview,列出全部 QuestSO,支持搜索/分类过滤;
  Play Mode 下读取 IQuestManager 运行时状态并着色显示;Edit Mode 高亮配置错误行;单击 Ping、双击 Select

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-25 08:06:54 +08:00
parent 8e88fc42e9
commit c7057db27d
6 changed files with 407 additions and 12 deletions

View File

@@ -74,6 +74,8 @@ namespace BaseGames.Dialogue
private DialogueSequenceSO _choiceBranchResult;
/// <summary>HandleChoices 子协程写入结果true = 分支深度超限,优雅降级(继续播放后续行)。</summary>
private bool _branchDepthExceeded;
/// <summary>当前正在播放对话的 NPC ID无对话时为 null。供外部系统主动查询"谁在说话"。</summary>
private string _currentNpcId;
// ── 复用 Yield 指令,避免协程每行 new WaitUntil 闭包 ───────────────
private sealed class WaitTypingOrSkip : CustomYieldInstruction
@@ -101,6 +103,8 @@ namespace BaseGames.Dialogue
private WaitForChoice _waitForChoice;
// 延迟 0.15s 防止玩家快速连击穿透跳过打字机后立即触发选项0
private WaitForSeconds _waitChoiceInputGuard;
// 超时守卫等待指令(与 _sequenceTimeoutSeconds 同步,在 Awake 初始化,避免每次 PlayImmediate 分配)
private WaitForSeconds _waitSequenceTimeout;
/// <summary>
/// 当 IsDialogueActive 时排队等待的对话请求。
@@ -113,6 +117,12 @@ namespace BaseGames.Dialogue
/// <summary>当前是否有对话正在播放。</summary>
public bool IsDialogueActive { get; private set; }
/// <summary>
/// 当前正在播放对话的 NPC ID。无对话活跃时为 <see langword="null"/>。
/// 供地图标记、HUD、分析埋点等外部系统主动查询"当前谁在说话",无需订阅事件。
/// </summary>
public string CurrentNpcId => _currentNpcId;
/// <summary>当前正在播放的对话优先级0 = 默认)。高优先级请求可打断低优先级。</summary>
private int _currentPriority;
@@ -129,6 +139,8 @@ namespace BaseGames.Dialogue
_waitSkip = new WaitSkip(this);
_waitForChoice = new WaitForChoice(this);
_waitChoiceInputGuard = new WaitForSeconds(0.15f);
if (_sequenceTimeoutSeconds > 0f)
_waitSequenceTimeout = new WaitForSeconds(_sequenceTimeoutSeconds);
}
private void OnDestroy()
@@ -230,6 +242,7 @@ namespace BaseGames.Dialogue
private void PlayImmediate(DialogueSequenceSO sequence, string npcId, int priority = 0)
{
IsDialogueActive = true;
_currentNpcId = npcId;
_currentPriority = priority;
_skipRequested = false;
_selectedChoiceIndex = -1;
@@ -249,7 +262,7 @@ namespace BaseGames.Dialogue
/// </summary>
private IEnumerator SequenceTimeoutGuard(string npcId)
{
yield return new WaitForSeconds(_sequenceTimeoutSeconds);
yield return _waitSequenceTimeout ?? new WaitForSeconds(_sequenceTimeoutSeconds);
if (!IsDialogueActive) yield break;
Debug.LogError(
$"[DialogueManager] 对话序列 (npcId='{npcId}') 超时 {_sequenceTimeoutSeconds}s 未结束," +
@@ -271,6 +284,7 @@ namespace BaseGames.Dialogue
_dialogueBox?.HideChoices();
_dialogueBox?.Hide();
IsDialogueActive = false;
_currentNpcId = null;
_currentPriority = 0;
_skipRequested = false;
_selectedChoiceIndex = -1;
@@ -424,6 +438,7 @@ namespace BaseGames.Dialogue
{
_dialogueBox?.Hide();
IsDialogueActive = false;
_currentNpcId = null;
_currentPriority = 0;
// 优先通过 _onDialogueEnded 事件让 InputManager 决定如何恢复输入;