#if UNITY_EDITOR using System; using System.Collections.Generic; using System.IO; using System.Linq; using Sirenix.OdinInspector; using UnityEditor; using UnityEngine; namespace Continentis.MainGame.Equipment { // ========================================================================= // EquipmentDataEditor // // 注意:此类已不再使用 [CustomEditor] 注解。 // Odin Inspector 会自动接管 EquipmentData(ScriptableObject)的 Inspector 渲染, // 无需任何自定义 Editor 类。本文件现主要用于承载 EquipmentData 的编辑器扩展分部类。 // ========================================================================= internal static class EquipmentDataEditorPlaceholder { // 保留此类以维持文件存在意义,可在此添加未来的编辑器工具方法。 } // ========================================================================= // partial class EquipmentData — 编辑器专属扩展 // // 包含: // 1. GetEquipmentLogicDropdownItems() — 装备逻辑类下拉列表(静态) // 2. OnEquipmentLogicClassSelected() — 选中逻辑类后自动填充字段的回调 // 3. GetAvailable*() — References 列表的资产名称选择器 // 4. 内部共享辅助方法(AssetDatabase 查询) // ========================================================================= public partial class EquipmentData { // ── 1. 装备逻辑类选择器 ──────────────────────────────────────────────── // 替代旧 EquipmentDataEditor 中的 DrawSearchableTypeSelector() // 配合 EquipmentData.cs 中 _classSelector 字段上的 // [ValueDropdown("@EquipmentData.GetEquipmentLogicDropdownItems()")] /// /// 为 [ValueDropdown] 提供所有 EquipmentBase 子类的层级下拉项。 /// 路径格式为 "ModName/Category/ClassName",与旧 TypeSelectorWindow 分组逻辑一致。 /// 此方法为 static,故在 [ValueDropdown] 中使用 @ 表达式语法引用。 /// public static IEnumerable> GetEquipmentLogicDropdownItems() { const string namespacePrefix = "Continentis.Mods"; const string namespaceToRemove = "Equipments"; IEnumerable types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => { try { return a.GetTypes(); } catch { return Type.EmptyTypes; } }) .Where(t => typeof(EquipmentBase).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface && t != typeof(EquipmentBase)); foreach (Type type in types.OrderBy(t => t.FullName)) { string path; if (type.Namespace != null && type.Namespace.StartsWith(namespacePrefix)) { List segments = type.Namespace .Substring(namespacePrefix.Length) .Split('.') .Where(s => !string.IsNullOrEmpty(s) && s != namespaceToRemove) .ToList(); path = segments.Count > 0 ? string.Join("/", segments) + "/" + type.Name : type.Name; } else { path = "Uncategorized/" + type.Name; } // value 存储类型的完整名称(FullName),便于在 OnValueChanged 中精确反查 yield return new ValueDropdownItem(path, type.FullName ?? type.Name); } } /// /// 当 classFullName 通过下拉菜单改变后,自动填充 className / modName / displayName。 /// 被 EquipmentData.cs 中 classFullName 字段上的 [OnValueChanged("OnEquipmentLogicClassSelected")] 触发。 /// private void OnEquipmentLogicClassSelected() { if (string.IsNullOrEmpty(classFullName)) return; // 根据 FullName 反查对应 Type Type selectedType = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => { try { return a.GetTypes(); } catch { return Type.EmptyTypes; } }) .FirstOrDefault(t => (t.FullName == classFullName || t.Name == classFullName) && typeof(EquipmentBase).IsAssignableFrom(t) && !t.IsAbstract); if (selectedType == null) return; const string prefix = "Continentis.Mods"; string ns = selectedType.Namespace ?? string.Empty; if (ns.StartsWith(prefix)) { // 例:ns = "Continentis.Mods.Basic.Equipments.Sword" // → afterPrefix = ".Basic.Equipments.Sword" (if prefix is without dot) // 统一处理点号 string afterPrefix = ns.Substring(prefix.Length).TrimStart('.'); string[] parts = afterPrefix.Split('.'); string resolvedMod = parts.Length > 0 ? parts[0] : string.Empty; // 移除 "Equipments" 层级并计算 className(短路径模式) string equipmentsSegment = "Equipments"; string resolvedCategory = ns.Contains(equipmentsSegment) ? ns.Split(new[] { equipmentsSegment }, StringSplitOptions.None).Last().TrimStart('.') : string.Empty; modName = resolvedMod; className = string.IsNullOrEmpty(resolvedCategory) ? selectedType.Name : resolvedCategory + "." + selectedType.Name; displayName = $"Equipment_{resolvedMod}_{selectedType.Name}_DisplayName"; } else { modName = string.Empty; className = selectedType.Name; displayName = $"Equipment_{selectedType.Name}_DisplayName"; } EditorUtility.SetDirty(this); Debug.Log($"[EquipmentData] 已自动填充 → modName: {modName}, className: {className}, " + $"displayName: {displayName}, classFullName: {classFullName}"); } // ── 2. References 列表的资产名称选择器 ────────────────────────────────── // 替代旧 DrawCharacterListGUI(prop) private IEnumerable> GetAvailablePrefabs() => GetAssetNameDropdown("t:Prefab"); private IEnumerable> GetAvailableCardData() => GetAssetNameDropdown("t:CardData"); private IEnumerable> GetAvailableCharacterData() => GetAssetNameDropdown("t:CharacterData"); // ── 内部共享辅助方法 ───────────────────────────────────────────────── /// /// 通过 AssetDatabase 搜索特定类型/标签的资产,将文件名(无扩展名)作为下拉项返回。 /// 用于 References 列表的字符串引用选择。 /// private static IEnumerable> GetAssetNameDropdown(string searchFilter) { string[] guids = AssetDatabase.FindAssets(searchFilter); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); string name = Path.GetFileNameWithoutExtension(path); yield return new ValueDropdownItem(name, name); } } } } #endif