- QuestSO: remove giverNpcId, prerequisiteQuests/Flags/FlagsLogic, failCondition, conditionFlags, npcDialogueKey fields; simplify GiverNpcId property to giverNpc?.npcId; clean ValidatePrerequisiteCycles/HasPrerequisiteCycle to use prerequisites.questDependencies; remove ValidateBranchDialogueKeys migration warning block; clean QuestPrerequisite doc - QuestManager: remove OnLoad DataVersion 1/2 migration paths (ProgressCounts, hasNewFormat/ useNewFormat); remove CheckQuestDepsAndFlags old-field fallback (prerequisiteQuests/Flags); remove UnlockBranches conditionFlags fallback; remove DispatchEvent failCondition fallback; fix ValidatePrerequisites DFS to scan prerequisites.questDependencies - SaveData: remove ProgressCounts (Obsolete), ObjectiveIndex (unused), GiverNpcId (never written) fields from QuestState; simplify DataVersion doc comment - QuestSOEditor: replace migration-only editor with minimal DrawDefaultInspector - QuestModule: update all prerequisiteQuests/conditionFlags/npcDialogueKey/failCondition references to canonical new fields; update ValidateBranchFlags check 10 - FlagAuditModule: replace conditionFlags/prerequisiteFlags scans with conditionFlagEntries/ prerequisites.flagCondition.flags - NpcSO: remove QuestSO.giverNpcId reference from npcId tooltip - NpcAffinityEvent/RewardSO: update doc comments to reference giverNpc instead of giverNpcId Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
92 lines
4.1 KiB
C#
92 lines
4.1 KiB
C#
using UnityEngine;
|
||
|
||
namespace BaseGames.Dialogue
|
||
{
|
||
/// <summary>
|
||
/// NPC 元数据资产(架构 14_NarrativeModule §2)。
|
||
/// 将 NPC 的唯一 ID、本地化名称 Key、头像、好感度上限集中在一处管理。
|
||
///
|
||
/// 关联:
|
||
/// • <see cref="InteractableNPC"/> 通过 _npcId 字段与此 SO 对应。
|
||
/// • <see cref="DialogueActorSO"/> 管理对话 UI 侧头像/颜色(二者可共享同一 Sprite,或独立维护)。
|
||
/// • <see cref="BaseGames.Quest.QuestSO"/> 的 <c>giverNpc</c> 字段直接引用此 SO,避免手填字符串。
|
||
///
|
||
/// 资产路径:Assets/_Game/Data/NPC/NPC_{npcId}.asset
|
||
/// </summary>
|
||
[CreateAssetMenu(menuName = "BaseGames/NPC/NPC")]
|
||
public class NpcSO : ScriptableObject
|
||
{
|
||
[Header("标识")]
|
||
[Tooltip("NPC 唯一 ID,如 \"NPC_Elder\"。需与 InteractableNPC._npcId 保持一致。")]
|
||
public string npcId;
|
||
|
||
[Header("显示")]
|
||
[Tooltip("本地化 Key,如 \"NPC_Elder_Name\"。通过 LocalizationManager 解析为实际名称。")]
|
||
public string nameKey;
|
||
[Tooltip("NPC 头像,用于地图、任务日志、DataHub 等 UI。")]
|
||
public Sprite portrait;
|
||
|
||
[Header("好感度")]
|
||
[Tooltip("该 NPC 的好感度上限(0 = 无上限)。\n" +
|
||
"QuestManager.CompleteQuest 发放 affinityBonus 时,不超过此数值。\n" +
|
||
"UI 侧可用此值绘制好感度进度条满格。")]
|
||
[Min(0)] public int maxAffinity = 0;
|
||
|
||
[Header("本地化")]
|
||
[Tooltip("nameKey 所在的本地化表名(默认 \"UI\")。\n" +
|
||
"若 NPC 名称存储在非默认表(如 \"Character\"),在此修改后 NpcSOEditor 预览和跳转按钮将使用正确的表。")]
|
||
public string localizationTable = "UI";
|
||
|
||
[Header("交互提示")]
|
||
[Tooltip("与此 NPC 交互时显示的提示本地化 Key(如 \"INTERACT_Talk\")。\n" +
|
||
"留空时 InteractableNPC 回退到内置字符串 \"对话\"。")]
|
||
public string interactPromptKey = "INTERACT_Talk";
|
||
|
||
#if UNITY_EDITOR
|
||
// npcId → 资产路径,5 秒 TTL,跨所有 NpcSO.OnValidate 共用,O(1) 重复检测。
|
||
private static System.Collections.Generic.Dictionary<string, string> s_npcIdToPath;
|
||
private static double s_npcIdsCacheTime = -10.0;
|
||
|
||
private static System.Collections.Generic.Dictionary<string, string> GetNpcIdCache()
|
||
{
|
||
double now = UnityEditor.EditorApplication.timeSinceStartup;
|
||
if (s_npcIdToPath != null && now - s_npcIdsCacheTime < 5.0)
|
||
return s_npcIdToPath;
|
||
|
||
s_npcIdToPath = new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.Ordinal);
|
||
string[] guids = UnityEditor.AssetDatabase.FindAssets("t:NpcSO");
|
||
foreach (var guid in guids)
|
||
{
|
||
var path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
||
var npc = UnityEditor.AssetDatabase.LoadAssetAtPath<NpcSO>(path);
|
||
if (npc != null && !string.IsNullOrEmpty(npc.npcId) && !s_npcIdToPath.ContainsKey(npc.npcId))
|
||
s_npcIdToPath[npc.npcId] = path;
|
||
}
|
||
s_npcIdsCacheTime = now;
|
||
return s_npcIdToPath;
|
||
}
|
||
|
||
private void OnValidate()
|
||
{
|
||
if (string.IsNullOrWhiteSpace(npcId))
|
||
{
|
||
UnityEditor.EditorUtility.SetDirty(this);
|
||
npcId = name;
|
||
}
|
||
|
||
var cache = GetNpcIdCache();
|
||
string myPath = UnityEditor.AssetDatabase.GetAssetPath(this);
|
||
if (!string.IsNullOrEmpty(myPath) &&
|
||
cache.TryGetValue(npcId, out var existingPath) &&
|
||
existingPath != myPath)
|
||
{
|
||
Debug.LogError(
|
||
$"[NpcSO] npcId '{npcId}' 与 " +
|
||
$"'{System.IO.Path.GetFileNameWithoutExtension(existingPath)}' 重复!请修改其中一个。", this);
|
||
s_npcIdsCacheTime = -10.0;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
}
|