Files
Continentis/Assets/Scripts/MainGame/Equipment/Editor/EquipmentDataEditor.cs
SoulliesOfficial d09b58fd80 架构大更
2026-03-20 11:56:50 -04:00

177 lines
8.0 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#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 会自动接管 EquipmentDataScriptableObject的 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()")]
/// <summary>
/// 为 [ValueDropdown] 提供所有 EquipmentBase 子类的层级下拉项。
/// 路径格式为 "ModName/Category/ClassName",与旧 TypeSelectorWindow 分组逻辑一致。
/// 此方法为 static故在 [ValueDropdown] 中使用 @ 表达式语法引用。
/// </summary>
public static IEnumerable<ValueDropdownItem<string>> GetEquipmentLogicDropdownItems()
{
const string namespacePrefix = "Continentis.Mods";
const string namespaceToRemove = "Equipments";
IEnumerable<Type> 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<string> 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<string>(path, type.FullName ?? type.Name);
}
}
/// <summary>
/// 当 classFullName 通过下拉菜单改变后,自动填充 className / modName / displayName。
/// 被 EquipmentData.cs 中 classFullName 字段上的 [OnValueChanged("OnEquipmentLogicClassSelected")] 触发。
/// </summary>
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<T>(prop)
private IEnumerable<ValueDropdownItem<string>> GetAvailablePrefabs()
=> GetAssetNameDropdown("t:Prefab");
private IEnumerable<ValueDropdownItem<string>> GetAvailableCardData()
=> GetAssetNameDropdown("t:CardData");
private IEnumerable<ValueDropdownItem<string>> GetAvailableCharacterData()
=> GetAssetNameDropdown("t:CharacterData");
// ── 内部共享辅助方法 ─────────────────────────────────────────────────
/// <summary>
/// 通过 AssetDatabase 搜索特定类型/标签的资产,将文件名(无扩展名)作为下拉项返回。
/// 用于 References 列表的字符串引用选择。
/// </summary>
private static IEnumerable<ValueDropdownItem<string>> 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<string>(name, name);
}
}
}
}
#endif