301 lines
13 KiB
C#
301 lines
13 KiB
C#
#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 会自动接管 CardData(ScriptableObject)的 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 端字段名仍为 endowingCurrentAttributes(CardData 端已用 [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 |