Files
Continentis/Assets/Scripts/MainGame/Character/Editor/CharacterDataEditor.cs
SoulliesOfficial ac98ec3aef 更新
2026-04-17 12:01:50 -04:00

230 lines
11 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 SLSUtilities.General;
using UnityEditor;
using UnityEngine;
namespace Continentis.MainGame.Character
{
// =========================================================================
// CharacterDataEditor
//
// 注意:此类已不再使用 [CustomEditor] 注解。
// Odin Inspector 会自动接管 CharacterDataScriptableObject的 Inspector 渲染,
// 无需任何自定义 Editor 类。本文件现主要用于承载 CharacterData 的编辑器扩展分部类。
// =========================================================================
internal static class CharacterDataEditorPlaceholder
{
// 保留此类以维持文件存在意义,可在此添加未来的编辑器工具方法。
}
// =========================================================================
// partial class CharacterData — 编辑器专属扩展
//
// 包含:
// 1. GetCharacterLogicDropdownItems() — 角色逻辑类下拉列表(静态)
// 2. OnCharacterLogicClassSelected() — 选中逻辑类后自动填充字段的回调
// 3. GetAvailable*() — References 列表的资产名称选择器
// 4. PasteDefaultAttributes() — 粘贴默认属性模板按钮
// 5. 内部共享辅助方法AssetDatabase 查询)
// =========================================================================
public partial class CharacterData
{
// ── 1. 角色逻辑类选择器 ────────────────────────────────────────────────
// 替代旧 CharacterDataEditor 中的 DrawSearchableTypeSelector()
// 配合 CharacterData.cs 中 classFullName 字段上的
// [ValueDropdown("@CharacterData.GetCharacterLogicDropdownItems()")]
/// <summary>
/// 为 [ValueDropdown] 提供所有 CharacterLogicBase 子类的层级下拉项。
/// 路径格式为 "ModName/Category/ClassName",与旧 TypeSelectorWindow 分组逻辑一致。
/// 此方法为 static故在 [ValueDropdown] 中使用 @ 表达式语法引用。
/// </summary>
public static IEnumerable<ValueDropdownItem<string>> GetCharacterLogicDropdownItems()
{
const string namespacePrefix = "Continentis.Mods";
const string namespaceToRemove = "Characters";
IEnumerable<Type> types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try { return a.GetTypes(); }
catch { return Type.EmptyTypes; }
})
.Where(t => typeof(CharacterLogicBase).IsAssignableFrom(t)
&& !t.IsAbstract
&& !t.IsInterface
&& t != typeof(CharacterLogicBase));
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 通过下拉菜单改变后,自动填充 modName / className。
/// 等价于旧 CharacterDataEditor 中 DrawSearchableTypeSelector 的 onTypeSelected 回调。
/// 被 CharacterData.cs 中字段上的 [OnValueChanged("OnCharacterLogicClassSelected")] 触发。
/// </summary>
private void OnCharacterLogicClassSelected()
{
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(CharacterLogicBase).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.Characters.Assassin"
// → afterPrefix = "Basic.Characters.Assassin"
// → parts = ["Basic", "Characters", "Assassin"]
string afterPrefix = ns.Substring(prefix.Length);
string[] parts = afterPrefix.Split('.');
string resolvedMod = parts.Length > 0 ? parts[0] : string.Empty;
string charactersSegment = resolvedMod + ".Characters";
string resolvedCategory = ns.Contains(charactersSegment)
? ns.Replace(prefix + charactersSegment, "").TrimStart('.')
: string.Empty;
modName = resolvedMod;
className = string.IsNullOrEmpty(resolvedCategory)
? selectedType.Name
: resolvedCategory + "." + selectedType.Name;
}
else
{
modName = string.Empty;
className = selectedType.Name;
}
EditorUtility.SetDirty(this);
Debug.Log($"[CharacterData] 已自动填充 → modName: {modName}, className: {className}, " +
$"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");
private IEnumerable<ValueDropdownItem<string>> GetAvailableHUDData()
=> GetAssetNameDropdown("t:HUDData");
// ── 3. 粘贴默认属性 ───────────────────────────────────────────────────
[BoxGroup("Data/属性/工具"), PropertyOrder(31)]
[Button("粘贴默认属性架构库", ButtonSizes.Medium)]
[GUIColor(0.7f, 1f, 0.7f)]
public void PasteDefaultAttributes()
{
if (generalAttributes == null) generalAttributes = new SLSUtilities.General.SerializedDictionary<string, float, CharacterAttributePair>();
if (runtimeGeneralAttributes == null) runtimeGeneralAttributes = new SLSUtilities.General.SerializedDictionary<string, string>();
generalAttributes.Clear();
runtimeGeneralAttributes.Clear();
var collections = Continentis.MainGame.Base.EditorBaseCollection.GetAllCollections().ToList();
if (collections.Count == 0)
{
Debug.LogWarning("[CharacterData] 未找到任何 EditorBaseCollection属性词典无法提取数据");
return;
}
foreach (var coll in collections)
{
if (coll.characterGeneralAttributes != null)
{
foreach (var kvp in coll.characterGeneralAttributes)
{
if (!generalAttributes.ContainsKey(kvp.Key))
{
float defaultVal = kvp.Key.Contains("Multiplier") ? 1f : 0f;
generalAttributes.Add(kvp.Key, defaultVal);
}
// 处理 runtimeGeneralAttributes 的特殊绑定 (如寻找含有 Maximum 的关联词条)
if (kvp.Key.Contains("Maximum"))
{
string baseKey = kvp.Key.Replace("Maximum", "");
if (coll.characterGeneralAttributes.ContainsKey(baseKey))
{
if (!runtimeGeneralAttributes.ContainsKey(baseKey))
{
runtimeGeneralAttributes.Add(baseKey, kvp.Key);
}
}
}
}
}
}
EditorUtility.SetDirty(this);
Debug.Log($"[CharacterData] '{this.name}' 提取完毕。从 {collections.Count} 个主库中提取了 {generalAttributes.Count} 个属性以及 {runtimeGeneralAttributes.Count} 个运行时约束!");
}
// ── 内部共享辅助方法 ─────────────────────────────────────────────────
/// <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