Files
Continentis/Assets/Scripts/MainGame/Card/Editor/CardDataEditor.cs
SoulliesOfficial c3b1561375 更新
2026-04-01 12:23:27 -04:00

301 lines
13 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 Continentis.MainGame.Base;
using Sirenix.OdinInspector;
using SLSFramework.General;
using UnityEditor;
using UnityEngine;
namespace Continentis.MainGame.Card
{
// =========================================================================
// CardDataEditor
//
// 注意:此类已不再使用 [CustomEditor] 注解。
// Odin Inspector 会自动接管 CardDataScriptableObject的 Inspector 渲染,
// 无需任何自定义 Editor 类。本文件现主要用于承载 CardData 的编辑器扩展分部类。
// 如需将来添加 OdinEditorWindow 工具或其他编辑器工具类,可在此文件中继续扩展。
// =========================================================================
internal static class CardDataEditorPlaceholder
{
// 保留此类以维持文件存在意义,可在此添加未来的编辑器工具方法。
}
// =========================================================================
// partial class CardData — 编辑器专属扩展
//
// 包含:
// 1. Odin ValueDropdown 数据提供方法
// 2. OnValueChanged 回调(自动填充 modName / categoryName / 文本Key
// 3. DrawCardSpriteField() — 16:9 自定义预览绘制器
// 4. PasteDefaultAttributes() — 粘贴默认属性模板按钮
// 5. OpenCardPreviewer() — 打开 CardPreviewer 工具窗口按钮
// 6. 内部共享辅助方法AssetDatabase 查询)
// =========================================================================
public partial class CardData
{
// ── 1. 卡牌逻辑类选择器 ───────────────────────────────────────────────
// 替代旧 CardDataEditor 中的 DrawSearchableTypeSelector()
// 配合 CardData.cs 中字段上的 [ValueDropdown("@CardData.GetCardLogicDropdownItems()")]
/// <summary>
/// 为 [ValueDropdown] 提供所有 CardLogicBase 子类的层级下拉项。
/// 路径格式为 "ModName/Category/ClassName",与旧 TypeSelectorWindow 分组逻辑一致。
/// 此方法为 static故在 [ValueDropdown] 中使用 @ 表达式语法引用。
/// </summary>
public static IEnumerable<ValueDropdownItem<string>> GetCardLogicDropdownItems()
{
const string namespacePrefix = "Continentis.Mods";
const string namespaceToRemove = "Cards";
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try
{
return a.GetTypes();
}
catch
{
return Type.EmptyTypes;
}
})
.Where(t => typeof(CardLogicBase).IsAssignableFrom(t)
&& !t.IsAbstract
&& !t.IsInterface
&& t != typeof(CardLogicBase));
foreach (var type in types.OrderBy(t => t.FullName))
{
string path;
if (type.Namespace != null && type.Namespace.StartsWith(namespacePrefix))
{
// 去掉公共前缀,拆分剩余命名空间段落
var 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;
}
yield return new ValueDropdownItem<string>(path, type.Name);
}
}
/// <summary>
/// 当 className 通过下拉菜单改变后,自动填充 modName / categoryName / displayName / functionText。
/// 等价于旧 CardDataEditor 中 DrawSearchableTypeSelector 的 onTypeSelected 回调。
/// 被 CardData.cs 中字段上的 [OnValueChanged("OnCardLogicClassSelected")] 触发。
/// </summary>
private void OnCardLogicClassSelected()
{
if (string.IsNullOrEmpty(className)) return;
// 根据 className 反查对应 Type仅首个匹配
var selectedType = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try
{
return a.GetTypes();
}
catch
{
return Type.EmptyTypes;
}
})
.FirstOrDefault(t =>
t.Name == className
&& typeof(CardLogicBase).IsAssignableFrom(t)
&& !t.IsAbstract);
if (selectedType?.Namespace == null) return;
const string prefix = "Continentis.Mods.";
var ns = selectedType.Namespace;
if (!ns.StartsWith(prefix)) return;
// 解析 Mod 名与分类名
// 例ns = "Continentis.Mods.Basic.Cards.Assassin"
// → afterPrefix = "Basic.Cards.Assassin"
// → parts = ["Basic", "Cards", "Assassin"]
var afterPrefix = ns.Substring(prefix.Length);
var parts = afterPrefix.Split('.');
var resolvedMod = parts.Length > 0 ? parts[0] : string.Empty;
// 去掉 "{ModName}.Cards" 后的剩余部分即为 categoryName
var cardsSegment = resolvedMod + ".Cards";
var resolvedCategory = ns.Contains(cardsSegment)
? ns.Replace(prefix + cardsSegment, "").TrimStart('.')
: string.Empty;
modName = resolvedMod;
categoryName = resolvedCategory;
displayName = $"Card_{resolvedMod}_{className}_DisplayName";
functionText = $"Card_{resolvedMod}_{className}_FunctionText";
descriptionText = $"Card_{resolvedMod}_{className}_Description";
EditorUtility.SetDirty(this);
Debug.Log($"[CardData] 已自动填充 → modName: {modName}, categoryName: {categoryName}, " +
$"className: {className}, displayName: {displayName}, functionText: {functionText}");
}
// ── 2. 关键词下拉 ─────────────────────────────────────────────────────
// 从所有 EditorBaseCollection 的 cardKeywords 字典聚合 Key 列表
private IEnumerable<ValueDropdownItem<string>> GetAvailableKeywords()
{
return EditorBaseCollection.GetCardKeywordsDropdown();
}
// ── 3. 意图图标下拉 ───────────────────────────────────────────────────
// 从所有 EditorBaseCollection 的 intentionIcons 字典聚合 Key 列表
private IEnumerable<ValueDropdownItem<string>> GetAvailableIntentionIcons()
{
var seen = new HashSet<string>(StringComparer.Ordinal);
foreach (var coll in EditorBaseCollection.GetAllCollections())
{
if (coll.intentionIcons == null) continue;
foreach (var key in coll.intentionIcons.Keys)
if (seen.Add(key))
yield return new ValueDropdownItem<string>(key, key);
}
}
// ── 4. intentionValueNames 候选(来自 variableAttributes 的 Key────────
// 替代旧 DrawListWithLocalSelector(intentionValueNamesProp, "variableAttributes")
private IEnumerable<ValueDropdownItem<string>> GetVariableAttributeKeys()
{
if (variableAttributes == null) yield break;
foreach (var key in variableAttributes.Keys)
yield return new ValueDropdownItem<string>(key, key);
}
// ── 5. References 列表的资产名称选择器 ──────────────────────────────────
// 替代旧 DrawCharacterListGUI<T>(prop)
private IEnumerable<ValueDropdownItem<string>> GetAvailablePrefabs()
{
return GetAssetNameDropdown("t:Prefab");
}
private IEnumerable<ValueDropdownItem<string>> GetAvailableCardData()
{
return GetAssetNameDropdown("t:CardData");
}
private IEnumerable<ValueDropdownItem<string>> GetAvailableCharacterData()
{
return GetAssetNameDropdown("t:CharacterData");
}
[BoxGroup("Display")]
[PropertyOrder(11)]
[Button("✨ 预览卡牌效果", ButtonSizes.Medium)]
[GUIColor(0.5f, 0.8f, 1f)]
private void OpenCardPreviewer()
{
CardPreviewer.OpenWith(this);
}
[BoxGroup("Data/属性/工具")]
[PropertyOrder(35)]
[Button("粘贴默认属性", ButtonSizes.Medium)]
[GUIColor(0.7f, 1f, 0.7f)]
private void PasteDefaultAttributes()
{
var targetCollections = new List<CardAttributesDefaultCollection>();
var guids = AssetDatabase.FindAssets("t:CardAttributesDefaultCollection");
if (guids.Length == 0)
{
Debug.LogWarning("[CardData] 未找到任何 CardAttributesDefaultCollection 资产。");
return;
}
if (guids.Length > 1) Debug.Log("[CardData] 找到多个 CardAttributesDefaultCollection将合并后导入。");
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var directory = Path.GetDirectoryName(path)?.Replace('\\', '/');
var collection =
AssetDatabase.LoadAssetAtPath<CardAttributesDefaultCollection>(path);
if (collection == null) continue;
// 只收集位于 Assets/Mods/{ModName}/Cards/DefaultCollections/ 路径下的资产
if (!string.IsNullOrEmpty(directory)
&& directory.StartsWith("Assets/Mods/")
&& directory.EndsWith("/Cards/DefaultCollections"))
{
targetCollections.Add(collection);
Debug.Log($"[CardData] 已加载默认集合:{path}");
}
}
if (targetCollections.Count == 0)
{
Debug.LogWarning("[CardData] 未在 Assets/Mods/{ModName}/Cards/DefaultCollections/ " +
"路径下找到有效的 CardAttributesDefaultCollection 资产。");
return;
}
// 合并各 Collection 中的数据
var merged_variable = new Dictionary<string, float>();
var merged_original = new Dictionary<string, float>();
// Collection 端字段名仍为 endowingCurrentAttributesCardData 端已用 [FormerlySerializedAs] 改名)
var merged_runtime = new Dictionary<string, string>();
foreach (var collection in targetCollections)
{
collection.variableAttributes?.PasteDictionary(merged_variable);
collection.originalAttributes?.PasteDictionary(merged_original);
collection.endowingCurrentAttributes?.PasteDictionary(merged_runtime);
}
// 写入当前 CardData
merged_variable.PasteDictionary(variableAttributes);
merged_original.PasteDictionary(originalAttributes);
merged_runtime.PasteDictionary(runtimeCurrentAttributes);
EditorUtility.SetDirty(this);
Debug.Log($"[CardData] 已将默认属性粘贴至 '{name}'。");
}
// ── 内部共享辅助方法 ─────────────────────────────────────────────────
/// <summary>
/// 通过 AssetDatabase 搜索特定类型/标签的资产,将文件名(无扩展名)作为下拉项返回。
/// 用于 References 列表的字符串引用选择。
/// </summary>
private static IEnumerable<ValueDropdownItem<string>> GetAssetNameDropdown(string searchFilter)
{
var guids = AssetDatabase.FindAssets(searchFilter);
foreach (var guid in guids)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
var name = Path.GetFileNameWithoutExtension(path);
yield return new ValueDropdownItem<string>(name, name);
}
}
}
}
#endif