using System; using System.Collections.Generic; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.Localization; using UnityEngine.Localization.Tables; namespace Ichni.Story { /// /// 全局 Helper 的独立数据资产。 /// 该资产不隶属于任何 StoryData,由 统一引用, /// 因而所有章节都共用同一个 Helper 角色、名称和候选对话池。 /// 运行时会在满足条件的候选中按权重抽取,并在候选数量允许时避免与上次对话重复; /// 具体抽取和 UI 展示由 负责。 /// [CreateAssetMenu(fileName = "StoryHelperData", menuName = "Ichni/Story/New/Story Helper Data")] public class StoryHelperData : SerializedScriptableObject { /// /// Helper 的稳定身份标识。当前阶段只有一个 Helper,但后续的更换界面、存档和统计 /// 都应使用该 ID,而非依赖可变的资源名称或显示名称。 /// [LabelText("Helper ID")] [Tooltip("使用小写英文、数字和下划线,例如 chapter0_helper。未来切换与持久化 Helper 时使用此稳定 ID。")] public string helperId; /// /// 当前静态版本使用的立绘。留空时,静态 Presenter 会保留场景中原本配置的 Image, /// 因而可以在不立即迁移全部美术引用的情况下逐步接入本数据资产。 /// [PreviewField(75, ObjectFieldAlignment.Left)] [LabelText("Portrait Sprite")] [Tooltip("当前静态 Helper 立绘。未来改用 Spine 或 Live2D 时,可由对应 Presenter 忽略该字段。")] public Sprite portraitSprite; /// /// Helper 名称与对话文本所使用的 Unity Localization String Table。 /// [LabelText("Localization Table")] [Tooltip("Display Name Key、Fallback Dialogue Key 与 Dialogue Pool 的 Text Key 均在此 String Table 中查找。")] public TableReference localizationTable; /// /// Helper 显示名称的 Unity Localization Entry Key。 /// [LabelText("Display Name Key (Localization)")] [Tooltip("Helper 显示名称使用的 Unity Localization Entry Key。")] public string displayNameKey; /// /// 当所有候选对话均不满足条件时显示的兜底对话 Entry Key。 /// [LabelText("Fallback Dialogue Key (Localization)")] [Tooltip("没有候选对话满足条件时显示的 Unity Localization Entry Key。")] public string fallbackDialogueKey; /// /// 全章节共用的候选对话。条件留空表示始终可参与抽取。 /// [LabelText("Dialogue Pool")] [ListDrawerSettings(ShowFoldout = true, DefaultExpandedState = false, DraggableItems = true, ShowItemCount = true)] [Tooltip("全章节共用的候选 Helper 对话。条件留空时该项始终可参与按权重抽取。")] public List dialoguePool = new List(); } /// /// Helper 对话池中的一条候选记录。 /// 条件复用故事树的 ,因此未来可直接根据章节剧情变量、 /// 路线变量或指定 Block 的完成状态筛选,而不需要为 Helper 另写一套条件语法。 /// [Serializable] [InlineProperty] [HideReferenceObjectPicker] public class StoryHelperDialogueDefinition { /// /// 候选对话的稳定 ID,仅用于调试、去重和未来数据追踪。 /// 命名使用小写英文、数字和下划线,例如 general_greeting_01。 /// [HorizontalGroup("Identity", 0.38f)] [LabelText("ID")] [LabelWidth(20)] [Tooltip("候选对话的稳定 ID,仅用于调试、去重和未来数据追踪。")] public string dialogueId; /// /// 实际显示内容的 Unity Localization Entry Key。 /// [HorizontalGroup("Identity")] [LabelText("Text Key")] [LabelWidth(52)] [Tooltip("实际显示文本的 Unity Localization Entry Key。")] public string dialogueKey; /// /// 此对话可被抽取的条件;留空表示始终可用。 /// [LabelText("Availability")] [Tooltip("此候选对话可参与抽取的条件;留空表示始终可用。")] public StoryCondition availabilityCondition = new StoryCondition(); /// /// 在所有满足条件的候选中的相对抽取权重。值越大,出现概率越高; /// 小于 1 的值在运行时应按 1 处理,避免无效的零或负权重。 /// [LabelText("Weight")] [MinValue(1)] [Tooltip("在满足条件的候选中使用的相对抽取权重,最小为 1。")] public int weight = 1; } }