Files
zeling_v2/Assets/_Game/Scripts/Quest/RewardSO.cs
Joywayer 6eaa83dc71 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>
2026-05-25 00:05:15 +08:00

86 lines
4.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 UnityEngine;
using BaseGames.Core.Events;
using BaseGames.Player;
namespace BaseGames.Quest
{
/// <summary>
/// 任务奖励 SO架构 22_QuestChallengeModule §4
/// 由 QuestManager.CompleteQuest() 调用 Apply() 发放奖励。
/// 资产路径: Assets/ScriptableObjects/Quest/Reward_{questId}.asset
/// </summary>
[CreateAssetMenu(menuName = "BaseGames/Quest/Reward")]
public class RewardSO : ScriptableObject
{
[Header("货币与属性")]
[Tooltip("完成任务奖励的灵珠数量0 = 不奖励)。")]
public int lingZhu;
[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 事件频道StringEventChannelSO。\n" +
"Apply() 调用时,每个 itemId 都会通过此频道广播,供 EquipmentManager/QuestManager 处理。\n" +
"未连线时物品奖励不生效。")]
[SerializeField] private StringEventChannelSO _onCollectiblePickup;
/// <summary>
/// 将奖励应用到游戏状态(由 QuestManager.CompleteQuest 调用)。
/// 通过 <see cref="IRewardTarget"/> 接口操作,避免直接依赖 BaseGames.Player 程序集。
/// </summary>
public void Apply(IRewardTarget target)
{
if (target == null) return;
if (lingZhu > 0) target.AddLingZhu(lingZhu);
if (soulBonus > 0) target.AddSoulPower(soulBonus);
if (unlocksAbility && unlockedAbilityFlag != AbilityType.None)
target.UnlockAbilityFlag((uint)unlockedAbilityFlag);
// 通过 EVT_CollectiblePickup 事件频道广播每个物品 ID
if (itemIds != null && itemIds.Length > 0)
{
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
}
}
}
}
}