feat: Round 48 narrative systems improvements

- QuestSO: Add ValidateBranchCycles() DFS detection for branches[].nextQuest loop
- QuestSO: Mark three legacy prerequisite fields with v2.0 removal warning in Tooltip
- IQuestManager: Add QuestLockReason enum + QuestLockInfo struct (strongly-typed lock info)
- IQuestManager: Add GetQuestLockInfo() method to interface; GetQuestLockReason() now delegates to it
- IQuestEventSource: Add OnQuestStateChanged(questId, oldState, newState) unified event
- QuestManager: Implement GetQuestLockInfo(); fire OnQuestStateChanged on all state transitions
- DialogueManager: Add one-frame yield in HandleChoices before ShowChoices (skip-debounce fix)
- DialogueManager: Increment _playbackId in ForceEnd() to invalidate residual choice callbacks
- DialogueSequenceSO: Add UNITY_EDITOR debug log in TryGetActiveVariant on variant match
- WorldStateRegistry: Add OnBatchStateChanged event + BatchMark() batch-write API
- DialogueModule: List badge shows warning indicator for unconditional-shadowing variants
- DialogueModule: BuildVariantsCard shows logic mode (AND/OR) alongside flag conditions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-05-25 00:05:15 +08:00
parent 446fd5dcd0
commit 6eaa83dc71
72 changed files with 7080 additions and 373 deletions

View File

@@ -1,5 +1,6 @@
using UnityEngine;
using BaseGames.Core.Events;
using BaseGames.Player;
namespace BaseGames.Quest
{
@@ -11,18 +12,42 @@ namespace BaseGames.Quest
[CreateAssetMenu(menuName = "BaseGames/Quest/Reward")]
public class RewardSO : ScriptableObject
{
public int lingZhu; // LingZhu 货币奖励
public int soulBonus; // 灵魂槽扩展(+MaxSoulPower
public string[] itemIds; // 物品/护符 ID 列表(通过 InventoryManager 发放)
public int affinityBonus; // 对发布 NPC 的好感度增量(存入 SaveData.World.NpcRelations
public string unlockDialogueKey; // 解锁 NPC 新台词集合 key架构 §4
[Header("货币与属性")]
[Tooltip("完成任务奖励的灵珠数量0 = 不奖励)。")]
public int lingZhu;
[Tooltip("是否解锁能力AbilityType 无 None 值,用 bool 标识)")]
public bool unlocksAbility; // ⚠️ AbilityType 无 None用 bool 标识
public uint unlockedAbilityFlag; // AbilityType 的 uint 位掩码值(仅当 unlocksAbility == true 有效)
[Tooltip("完成任务后永久增加的灵魂槽上限数值0 = 不奖励)。")]
public int soulBonus;
[Header("物品")]
[Tooltip("完成任务奖励的物品/护符 ID 列表。每个 ID 通过 EVT_CollectiblePickup 事件频道广播,由 InventoryManager 处理。\n" +
"格式如 [\"Charm_DashBoost\", \"Item_HealShard\"]")]
public string[] itemIds;
[Header("NPC 关系")]
[Tooltip("完成任务后对 giverNpcIdQuestSO 中配置)的好感度增量。\n" +
"正值=好感增加,负值=好感降低。0 = 不影响好感度。\n" +
"增量以强类型 NpcAffinityEventnpcId + delta + newTotal广播至 EVT_NpcAffinityChanged\n" +
"并持久化到 SaveData.World.NpcRelations。接收方无需字符串解析。")]
public int affinityBonus;
[Tooltip("完成任务后解锁的 NPC 台词集合 Key格式如 \"DLG_Elder_PostQuest\")。\n" +
"非空时广播 EVT_DialogueKeyUnlocked 事件,供 NPC 台词管理系统监听并切换对话集。\n" +
"留空表示不解锁新台词。")]
public string unlockDialogueKey;
[Header("能力解锁")]
[Tooltip("勾选后unlockedAbilityFlag 中指定的能力将在完成任务时解锁。")]
public bool unlocksAbility;
[Tooltip("要解锁的能力AbilityType 组合标志位,仅当 unlocksAbility == true 有效)。\n" +
"可多选,支持组合解锁多项能力。")]
public AbilityType unlockedAbilityFlag;
[Header("物品发放事件")]
[Tooltip("EVT_CollectiblePickup:向 QuestManager/EquipmentManager 广播 itemId")]
[Tooltip("EVT_CollectiblePickup 事件频道StringEventChannelSO。\n" +
"Apply() 调用时,每个 itemId 都会通过此频道广播,供 EquipmentManager/QuestManager 处理。\n" +
"未连线时物品奖励不生效。")]
[SerializeField] private StringEventChannelSO _onCollectiblePickup;
/// <summary>
@@ -35,14 +60,25 @@ namespace BaseGames.Quest
if (lingZhu > 0) target.AddLingZhu(lingZhu);
if (soulBonus > 0) target.AddSoulPower(soulBonus);
if (unlocksAbility && unlockedAbilityFlag != 0)
target.UnlockAbilityFlag(unlockedAbilityFlag);
if (unlocksAbility && unlockedAbilityFlag != AbilityType.None)
target.UnlockAbilityFlag((uint)unlockedAbilityFlag);
// 通过 EVT_CollectiblePickup 事件频道广播每个物品 ID
if (itemIds != null && _onCollectiblePickup != null)
if (itemIds != null && itemIds.Length > 0)
{
foreach (var id in itemIds)
_onCollectiblePickup.Raise(id);
if (_onCollectiblePickup != null)
{
foreach (var id in itemIds)
_onCollectiblePickup.Raise(id);
}
else
{
#if UNITY_EDITOR || DEVELOPMENT_BUILD
Debug.LogWarning(
$"[RewardSO] '{name}' 配置了 {itemIds.Length} 个 itemIds" +
"但 _onCollectiblePickup 事件频道未连线,物品奖励不会发放。请在 Inspector 中指定 EVT_CollectiblePickup 频道。", this);
#endif
}
}
}
}