using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using SLSUtilities.UModAssistance; using SLSUtilities.General; using UnityEngine; #if UNITY_EDITOR using Sirenix.OdinInspector.Editor; #endif namespace Continentis.MainGame.Base { [CreateAssetMenu(fileName = "EditorBaseCollection", menuName = "Continentis/BaseCollections/EditorBaseCollection")] public partial class EditorBaseCollection : BaseCollection { [Title("角色")] [Tooltip("角色属性\nKey 为词条 ID(如 Health),Value 为词条中文描述(如 角色生命值)")] [SerializedDictionarySettings("Key", "Description")] public SerializedDictionary characterGeneralAttributes = new SerializedDictionary(); [Title("卡牌")] [Tooltip("卡牌关键词\nKey 为词条 ID(如 Retain),Value 为词条中文描述(如 保留)")] [SerializedDictionarySettings("Key", "Description")] public SerializedDictionary cardKeywords = new SerializedDictionary(); [SerializedDictionarySettings("Key", "Description")] [Tooltip("卡牌属性\nKey 为词条 ID(如 StaminaCost),Value 为词条中文描述(如 体力消耗)")] public SerializedDictionary cardAttributes = new SerializedDictionary(); [SerializedDictionarySettings("Key", "Sprite")] [Tooltip("意图图标\nKey 为意图 ID(如 Attack),Value 为对应的 Sprite 图标")] public SerializedDictionary intentionIcons = new SerializedDictionary(); /// /// 运行时:从 Resources 主包 + ModManager.Database 聚合所有已加载 Mod 的 EditorBaseCollection。 /// public static IEnumerable GetAllCollectionsRuntime() { // 主工程自带(放在 Resources 文件夹) var main = Resources.Load("EditorBaseCollection"); if (main != null) yield return main; // 各 Mod 通过 ModManifest.SaveToDatabase 注册进来的 if (ModManager.Database.TryGetValue(typeof(EditorBaseCollection), out var dict)) { foreach (var so in dict.Values) { if (so is EditorBaseCollection coll && coll != main) yield return coll; } } } /// /// 运行时:按 key 从所有已加载 Mod 的 EditorBaseCollection 中查找意图图标 Sprite。 /// 主工程优先,Mod 按加载顺序兜底。未找到时返回 null。 /// public static Sprite GetIntentionIcon(string key) { foreach (var coll in GetAllCollectionsRuntime()) { if (coll.intentionIcons != null && coll.intentionIcons.TryGetValue(key, out Sprite sprite)) return sprite; } return null; } #if UNITY_EDITOR /// /// 提供给包含 CharacterAttributePair 的字典使用的下拉菜单获取方法 /// 支持全局扫描所有 Mod 的 EditorBaseCollection /// public static IEnumerable> GetCharacterAttributesDropdown(InspectorProperty property) { List> allItems = new List>(); foreach (var coll in GetAllCollections()) { if (coll.characterGeneralAttributes != null) { allItems.AddRange(coll.characterGeneralAttributes.Select(kvp => new ValueDropdownItem { Text = $"{kvp.Key} ({kvp.Value})", Value = kvp.Key })); } } // 去重(防止某些 Mod 覆盖/重复属性在展示时出现冗余项) allItems = allItems.GroupBy(x => x.Value).Select(g => g.First()).ToList(); // 尝试排除已使用的 Key try { object dict = SerializedDictionaryHelper.GetDictionaryFromKey(property); if (dict is IEnumerable> existingDict) { var usedKeys = existingDict.Select(k => k.Key).ToHashSet(); string currentKey = property.ValueEntry.WeakSmartValue as string; // 显示未使用的项或者当前选中的项(即使已使用) return allItems.Where(x => !usedKeys.Contains(x.Value) || x.Value == currentKey); } } catch { // 如果 SerializedDictionaryHelper 提取失败,就退化为显示全部,不打断使用 } return allItems; } public static IEnumerable GetAllCollections() { string[] guids = UnityEditor.AssetDatabase.FindAssets($"t:{nameof(EditorBaseCollection)}"); foreach (string guid in guids) { string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid); EditorBaseCollection coll = UnityEditor.AssetDatabase.LoadAssetAtPath(path); if (coll != null) yield return coll; } } [BoxGroup("代码生成")] [Tooltip("如果为空,则默认命名空间为 Continentis.MainGame;填写 Mod 简称则生成为 Continentis.Mods.<简称>")] [LabelText("目标 Mod 简称 (如 Basic)")] public string targetModName = "Basic"; [BoxGroup("代码生成")] [Button("⚡ 生成 CharacterAttributes 静态脚本", ButtonSizes.Medium)] [GUIColor(0.6f, 0.9f, 1f)] public void GenerateAttributesScript() { string cleanModName = string.IsNullOrWhiteSpace(targetModName) ? string.Empty : targetModName.Trim(); string ns = string.IsNullOrEmpty(cleanModName) ? "Continentis.MainGame.Character" : $"Continentis.Mods.{cleanModName}.Character"; string className = string.IsNullOrEmpty(cleanModName) ? "CharacterAttributes" : $"{cleanModName}_CharacterAttributes"; string defaultPath = string.IsNullOrEmpty(cleanModName) ? Application.dataPath + "/Scripts/MainGame/Character/CharacterData" : Application.dataPath + $"/Mods/{cleanModName}/Scripts"; if (!System.IO.Directory.Exists(defaultPath)) { defaultPath = Application.dataPath; } string savePath = UnityEditor.EditorUtility.SaveFilePanel("保存生成的常量脚本", defaultPath, className + ".cs", "cs"); if (string.IsNullOrEmpty(savePath)) return; System.Text.StringBuilder sb = new System.Text.StringBuilder(); if(!string.IsNullOrEmpty(cleanModName)) { sb.AppendLine("using Continentis.MainGame;"); sb.AppendLine(); } sb.AppendLine($"namespace {ns}"); sb.AppendLine("{"); sb.AppendLine(" /// "); sb.AppendLine($" /// 基于 {this.name} 自动生成的角色属性常量字典。"); sb.AppendLine(" /// 包含所有配置的 Key,以防止手写出现 Typo。"); sb.AppendLine(" /// "); sb.AppendLine(" [GameAttributeCollection]"); sb.AppendLine($" public static class {className}"); sb.AppendLine(" {"); // 生成属性 sb.AppendLine(" // ── 属性 (General) ───────────────────────────────"); if (characterGeneralAttributes != null) { foreach (var kvp in characterGeneralAttributes) { if (string.IsNullOrWhiteSpace(kvp.Key)) continue; if (!string.IsNullOrWhiteSpace(kvp.Value)) { sb.AppendLine($" /// {kvp.Value} "); } sb.AppendLine($" public const string {kvp.Key} = \"{kvp.Key}\";"); sb.AppendLine(); } } // 移除最后一个多余的空行 if (sb.Length >= 2) sb.Length -= 2; sb.AppendLine(" }"); sb.AppendLine("}"); System.IO.File.WriteAllText(savePath, sb.ToString(), System.Text.Encoding.UTF8); UnityEditor.AssetDatabase.Refresh(); Debug.Log($"[代码生成] 已成功生成常量脚本:{savePath}"); } [BoxGroup("代码生成")] [Button("⚡ 生成 CardKeywords 静态脚本", ButtonSizes.Medium)] [GUIColor(0.6f, 1f, 0.8f)] public void GenerateCardKeywordsScript() { string cleanModName = string.IsNullOrWhiteSpace(targetModName) ? string.Empty : targetModName.Trim(); string ns = string.IsNullOrEmpty(cleanModName) ? "Continentis.MainGame.Card" : $"Continentis.Mods.{cleanModName}.Card"; string className = string.IsNullOrEmpty(cleanModName) ? "CardKeywords" : $"{cleanModName}_CardKeywords"; string defaultPath = string.IsNullOrEmpty(cleanModName) ? Application.dataPath + "/Scripts/MainGame/Card" : Application.dataPath + $"/Mods/{cleanModName}/Scripts"; if (!System.IO.Directory.Exists(defaultPath)) defaultPath = Application.dataPath; string savePath = UnityEditor.EditorUtility.SaveFilePanel("保存生成的关键词脚本", defaultPath, className + ".cs", "cs"); if (string.IsNullOrEmpty(savePath)) return; System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.AppendLine($"namespace {ns}"); sb.AppendLine("{"); sb.AppendLine(" /// "); sb.AppendLine($" /// 基于 {this.name} 自动生成的卡牌关键词常量字典。"); sb.AppendLine(" /// 包含所有配置的 Key,以防止手写出现 Typo。"); sb.AppendLine(" /// "); sb.AppendLine($" public static class {className}"); sb.AppendLine(" {"); if (cardKeywords != null) { foreach (var kvp in cardKeywords) { if (string.IsNullOrWhiteSpace(kvp.Key)) continue; if (!string.IsNullOrWhiteSpace(kvp.Value)) sb.AppendLine($" /// {kvp.Value} "); sb.AppendLine($" public const string {kvp.Key} = \"{kvp.Key}\";"); sb.AppendLine(); } } if (sb.Length >= 2) sb.Length -= 2; sb.AppendLine(" }"); sb.AppendLine("}"); System.IO.File.WriteAllText(savePath, sb.ToString(), System.Text.Encoding.UTF8); UnityEditor.AssetDatabase.Refresh(); Debug.Log($"[代码生成] 已成功生成关键词脚本:{savePath}"); } [BoxGroup("代码生成")] [Button("⚡ 生成 CardAttributes 静态脚本", ButtonSizes.Medium)] [GUIColor(0.6f, 1f, 0.8f)] public void GenerateCardAttributesScript() { string cleanModName = string.IsNullOrWhiteSpace(targetModName) ? string.Empty : targetModName.Trim(); string ns = string.IsNullOrEmpty(cleanModName) ? "Continentis.MainGame.Card" : $"Continentis.Mods.{cleanModName}.Card"; string className = string.IsNullOrEmpty(cleanModName) ? "CardAttributes" : $"{cleanModName}_CardAttributes"; string defaultPath = string.IsNullOrEmpty(cleanModName) ? Application.dataPath + "/Scripts/MainGame/Card" : Application.dataPath + $"/Mods/{cleanModName}/Scripts"; if (!System.IO.Directory.Exists(defaultPath)) defaultPath = Application.dataPath; string savePath = UnityEditor.EditorUtility.SaveFilePanel("保存生成的卡牌属性脚本", defaultPath, className + ".cs", "cs"); if (string.IsNullOrEmpty(savePath)) return; System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.AppendLine($"namespace {ns}"); sb.AppendLine("{"); sb.AppendLine(" /// "); sb.AppendLine($" /// 基于 {this.name} 自动生成的卡牌属性常量字典。"); sb.AppendLine(" /// 包含所有配置的 Key,以防止手写出现 Typo。"); sb.AppendLine(" /// "); sb.AppendLine($" public static class {className}"); sb.AppendLine(" {"); if (cardAttributes != null) { foreach (var kvp in cardAttributes) { if (string.IsNullOrWhiteSpace(kvp.Key)) continue; if (!string.IsNullOrWhiteSpace(kvp.Value)) sb.AppendLine($" /// {kvp.Value} "); sb.AppendLine($" public const string {kvp.Key} = \"{kvp.Key}\";"); sb.AppendLine(); } } if (sb.Length >= 2) sb.Length -= 2; sb.AppendLine(" }"); sb.AppendLine("}"); System.IO.File.WriteAllText(savePath, sb.ToString(), System.Text.Encoding.UTF8); UnityEditor.AssetDatabase.Refresh(); Debug.Log($"[代码生成] 已成功生成卡牌属性脚本:{savePath}"); } /// /// 提供给 CardData keywords 列表使用的关键词下拉菜单。 /// 聚合所有 Mod 的 EditorBaseCollection 中的 cardKeywords 字典。 /// public static IEnumerable> GetCardKeywordsDropdown() { HashSet seen = new HashSet(System.StringComparer.Ordinal); foreach (var coll in GetAllCollections()) { if (coll.cardKeywords == null) continue; foreach (var kvp in coll.cardKeywords) { if (string.IsNullOrWhiteSpace(kvp.Key)) continue; if (seen.Add(kvp.Key)) yield return new ValueDropdownItem($"{kvp.Key} ({kvp.Value})", kvp.Key); } } } /// /// 提供给 CardData 属性字典使用的属性名下拉菜单。 /// 聚合所有 Mod 的 EditorBaseCollection 中的 cardAttributes 字典。 /// public static IEnumerable> GetCardAttributesDropdown() { HashSet seen = new HashSet(System.StringComparer.Ordinal); foreach (var coll in GetAllCollections()) { if (coll.cardAttributes == null) continue; foreach (var kvp in coll.cardAttributes) { if (string.IsNullOrWhiteSpace(kvp.Key)) continue; if (seen.Add(kvp.Key)) yield return new ValueDropdownItem($"{kvp.Key} ({kvp.Value})", kvp.Key); } } } private void OpenAttributeGenerator() { GenerateAttributesScript(); } #endif } }