#if UNITY_EDITOR using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine; using SLSFramework.UModAssistance; using Continentis.MainGame.Character; using Continentis.Mods; namespace Continentis.MainGame.Card { [CustomEditor(typeof(CardData))] public partial class CardDataEditor : DataEditor { // 存储我们需要自定义绘制的属性的引用 private SerializedProperty modNameProp; private SerializedProperty categoryNameProp; private SerializedProperty classNameProp; private SerializedProperty displayNameProp; private SerializedProperty cardRarityProp; private SerializedProperty cardTypeProp; private SerializedProperty keywordsProp; private SerializedProperty cardSpriteProp; private SerializedProperty cardLayoutTagsProp; private SerializedProperty functionTextProp; private SerializedProperty cardDescriptionProp; private SerializedProperty baseWeightProp; private SerializedProperty variableAttributesProp; private SerializedProperty originalAttributesProp; private SerializedProperty runtimeCurrentAttributesProp; private SerializedProperty upgradeNodeProp; private SerializedProperty prefabsProp; private SerializedProperty derivativeCardsProp; private SerializedProperty derivativeCharactersProp; protected override void OnEnable() { base.OnEnable(); LoadAllKeywordsFromEditRefs(); // 在启用时,根据我们修改后的字段名找到对应的SerializedProperty modNameProp = serializedObject.FindProperty("modName"); classNameProp = serializedObject.FindProperty("className"); categoryNameProp = serializedObject.FindProperty("categoryName"); displayNameProp = serializedObject.FindProperty("displayName"); cardRarityProp = serializedObject.FindProperty("cardRarity"); cardTypeProp = serializedObject.FindProperty("cardType"); keywordsProp = serializedObject.FindProperty("keywords"); cardSpriteProp = serializedObject.FindProperty("cardSprite"); cardLayoutTagsProp = serializedObject.FindProperty("cardLayoutTags"); functionTextProp = serializedObject.FindProperty("functionText"); cardDescriptionProp = serializedObject.FindProperty("cardDescription"); baseWeightProp = serializedObject.FindProperty("baseWeight"); variableAttributesProp = serializedObject.FindProperty("variableAttributes"); originalAttributesProp = serializedObject.FindProperty("originalAttributes"); runtimeCurrentAttributesProp = serializedObject.FindProperty("runtimeCurrentAttributes"); upgradeNodeProp = serializedObject.FindProperty("upgradeNode"); prefabsProp = serializedObject.FindProperty("prefabRefs"); derivativeCardsProp = serializedObject.FindProperty("derivativeCardDataRefs"); derivativeCharactersProp = serializedObject.FindProperty("derivativeCharacterDataRefs"); } public override void OnInspectorGUI() { serializedObject.Update(); // --- 绘制自定义的Type选择器 --- // 我们把它从所有自动绘制的属性中分离出来,放在最前面或最后面,让布局更清晰 EditorGUILayout.Space(); // 增加一点间距 EditorGUILayout.LabelField("Fundamental", EditorStyles.boldLabel); DrawSearchableTypeSelector( classNameProp, // 1. 存储类名的字符串属性 "Card Logic Class", // 2. 显示的标签 typeof(CardLogicBase), // 3. 要搜索的基类 (outType) => // 4. 【关键】当用户选择后执行的回调 { string className = outType.Name; string modName = outType.Namespace!.Replace("Continentis.Mods.", "").Split('.')[0]; string categoryName = outType.Namespace!.Replace("Continentis.Mods." + modName + ".Cards", ""); if (!string.IsNullOrEmpty(categoryName)) { categoryName = outType.Namespace!.Replace("Continentis.Mods." + modName + ".Cards", "").Substring(1); // 去掉开头的点 } string displayName = "Card_" + modName + "_" + className + "_DisplayName"; string functionTextName = "Card_" + modName + "_" + className + "_FunctionText"; classNameProp.stringValue = className; modNameProp.stringValue = modName; categoryNameProp.stringValue = categoryName; displayNameProp.stringValue = displayName; functionTextProp.stringValue = functionTextName; Debug.Log(outType.FullName); Debug.Log($"modName: {modName}, categoryName: {categoryName}, className: {className}, displayName: {displayName}, functionText: {functionTextName}"); serializedObject.ApplyModifiedProperties(); }, "Continentis.Mods", // 5. 你的 namespacePrefix "Cards" // 6. 你的 namespaceToRemove (注意:根据你的代码,这里不应包含".") ); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.PropertyField(modNameProp); EditorGUILayout.PropertyField(categoryNameProp); EditorGUILayout.PropertyField(classNameProp); EditorGUILayout.PropertyField(displayNameProp); EditorGUI.EndDisabledGroup(); EditorGUILayout.PropertyField(cardRarityProp); EditorGUILayout.PropertyField(cardTypeProp); EditorGUILayout.PropertyField(keywordsProp, true); if (keywordsProp.isExpanded) { DrawKeywordSelector(); } EditorGUILayout.PropertyField(cardSpriteProp); EditorGUILayout.PropertyField(cardLayoutTagsProp, true); EditorGUILayout.PropertyField(functionTextProp); EditorGUILayout.PropertyField(cardDescriptionProp); EditorGUILayout.Space(); EditorGUILayout.LabelField("Attributes", EditorStyles.boldLabel); EditorGUILayout.PropertyField(baseWeightProp); EditorGUILayout.PropertyField(variableAttributesProp, true); EditorGUILayout.PropertyField(originalAttributesProp, true); EditorGUILayout.PropertyField(runtimeCurrentAttributesProp, true); EditorGUILayout.Space(); EditorGUILayout.LabelField("Upgrade", EditorStyles.boldLabel); EditorGUILayout.PropertyField(upgradeNodeProp); // --- 绘制自定义的引用列表 --- EditorGUILayout.Space(); EditorGUILayout.LabelField("References", EditorStyles.boldLabel); DrawCharacterListGUI(prefabsProp); DrawCharacterListGUI(derivativeCardsProp); DrawCharacterListGUI(derivativeCharactersProp); HandleObjectPicker(); serializedObject.ApplyModifiedProperties(); } } public partial class CardDataEditor { private List allAvailableKeywords; /// /// 使用 AssetDatabase 查找项目中的所有 KeywordDatabase 并合并它们的列表 /// private void LoadAllKeywordsFromEditRefs() { // 使用 HashSet 来自动处理重复的关键词 HashSet allKeywordsSet = new HashSet(); // 1. 查找项目中所有类型为 "KeywordDatabase" 的 // "t:KeywordDatabase" 是一个搜索过滤器,t: 表示按类型搜索 string[] guids = AssetDatabase.FindAssets("t:ModEditReference"); foreach (string guid in guids) { // 2. 将 GUID 转换为资产路径 string assetPath = AssetDatabase.GUIDToAssetPath(guid); // 3. 加载该路径下的 ScriptableObject ModEditReference editRef = AssetDatabase.LoadAssetAtPath(assetPath); EditKeywordsReference cardKeywordsRef = editRef.cardKeywordsReference; if (cardKeywordsRef != null && cardKeywordsRef.keywordRefs != null) { // 4. 将词库中的所有词添加到 Set 中 foreach (string keyword in cardKeywordsRef.keywordRefs.Keys) { if (!string.IsNullOrEmpty(keyword)) { allKeywordsSet.Add(keyword); } } } } // 5. 将 Set 转换为 List 并排序,以便在菜单中清晰显示 allAvailableKeywords = allKeywordsSet.ToList(); allAvailableKeywords.Sort(); } private void DrawKeywordSelector() { // 1. 检查是否找到了任何关键词 if (allAvailableKeywords == null || allAvailableKeywords.Count == 0) { EditorGUILayout.HelpBox("在项目中没有找到任何 KeywordDatabase,或者词库为空。", MessageType.Warning); // 显示一个刷新按钮,以防用户刚刚创建了词库 if (GUILayout.Button("Refresh Keyword Edit References")) { LoadAllKeywordsFromEditRefs(); } return; } List currentKeywords = (target as CardData)!.keywords; // 2. 找出尚未添加的关键词 List keywordsToAdd = allAvailableKeywords.Except(currentKeywords).ToList(); // 3. 如果所有关键词都添加了,显示提示 if (keywordsToAdd.Count == 0) { EditorGUILayout.HelpBox("所有可用的关键词都已添加。", MessageType.Info); } else { // 4. 绘制 "Add Keyword" 按钮 if (GUILayout.Button("Add Keyword from Edit Refs...")) { GenericMenu menu = new GenericMenu(); foreach (string keyword in keywordsToAdd) { menu.AddItem(new GUIContent(keyword), false, () => { AddKeywordToList(keyword); }); } menu.ShowAsContext(); } } // 5. (可选) 添加一个手动刷新按钮 // 因为 AssetDatabase.FindAssets() 速度很快,所以我们也可以在每次GUI绘制时都调用 // 但如果词库很多,使用按钮刷新更好。 /*if (GUILayout.Button("Refresh Keyword Databases")) { LoadAllKeywordsFromEditRefs(); }*/ } private void AddKeywordToList(string keyword) { keywordsProp.InsertArrayElementAtIndex(keywordsProp.arraySize); SerializedProperty newElement = keywordsProp.GetArrayElementAtIndex(keywordsProp.arraySize - 1); newElement.stringValue = keyword; serializedObject.ApplyModifiedProperties(); } } } #endif