#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Continentis.MainGame.Base;
using Sirenix.OdinInspector;
using SLSUtilities.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()")]
///
/// 为 [ValueDropdown] 提供所有 CardLogicBase 子类的层级下拉项。
/// 路径格式为 "ModName/Category/ClassName",与旧 TypeSelectorWindow 分组逻辑一致。
/// 此方法为 static,故在 [ValueDropdown] 中使用 @ 表达式语法引用。
///
public static IEnumerable> 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(path, type.Name);
}
}
///
/// 当 className 通过下拉菜单改变后,自动填充 modName / categoryName / displayName / functionText。
/// 等价于旧 CardDataEditor 中 DrawSearchableTypeSelector 的 onTypeSelected 回调。
/// 被 CardData.cs 中字段上的 [OnValueChanged("OnCardLogicClassSelected")] 触发。
///
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> GetAvailableKeywords()
{
return EditorBaseCollection.GetCardKeywordsDropdown();
}
// ── 3. 意图图标下拉 ───────────────────────────────────────────────────
// 从所有 EditorBaseCollection 的 intentionIcons 字典聚合 Key 列表
private IEnumerable> GetAvailableIntentionIcons()
{
var seen = new HashSet(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(key, key);
}
}
// ── 4. intentionValueNames 候选(来自 variableAttributes 的 Key)────────
// 替代旧 DrawListWithLocalSelector(intentionValueNamesProp, "variableAttributes")
private IEnumerable> GetVariableAttributeKeys()
{
if (variableAttributes == null) yield break;
foreach (var key in variableAttributes.Keys)
yield return new ValueDropdownItem(key, key);
}
// ── 5. References 列表的资产名称选择器 ──────────────────────────────────
// 替代旧 DrawCharacterListGUI(prop)
private IEnumerable> GetAvailablePrefabs()
{
return GetAssetNameDropdown("t:Prefab");
}
private IEnumerable> GetAvailableCardData()
{
return GetAssetNameDropdown("t:CardData");
}
private IEnumerable> GetAvailableCharacterData()
{
return GetAssetNameDropdown("t:CharacterData");
}
[BoxGroup("Fundamental")]
[PropertyOrder(6)]
[Button("📄 打开逻辑脚本", ButtonSizes.Medium)]
[GUIColor(1f, 0.92f, 0.6f)]
[ShowIf("@!string.IsNullOrEmpty(className)")]
private void OpenCardLogicScript()
{
if (string.IsNullOrEmpty(className))
{
Debug.LogWarning("[CardData] 尚未选择逻辑类,无法打开对应脚本。");
return;
}
// 根据 modName / categoryName / className 拼接可能的文件路径
// 路径格式: Assets/Mods/{modName}/Cards/Scripts/{categoryName}/{className}.cs
// | Assets/Mods/{modName}/Cards/Scripts/{className}.cs (无分类时)
var candidatePaths = new List();
if (!string.IsNullOrEmpty(categoryName))
candidatePaths.Add($"Assets/Mods/{modName}/Cards/Scripts/{categoryName}/{className}.cs");
candidatePaths.Add($"Assets/Mods/{modName}/Cards/Scripts/{className}.cs");
// 在全项目中也搜一遍(应对目录结构不规范的情况)
string[] guids = AssetDatabase.FindAssets($"{className} t:MonoScript");
foreach (string guid in guids)
{
string p = AssetDatabase.GUIDToAssetPath(guid);
if (Path.GetFileNameWithoutExtension(p) == className)
candidatePaths.Add(p);
}
foreach (string path in candidatePaths)
{
var script = AssetDatabase.LoadAssetAtPath(path);
if (script != null)
{
AssetDatabase.OpenAsset(script);
EditorGUIUtility.PingObject(script);
Debug.Log($"[CardData] 已打开逻辑脚本:{path}");
return;
}
}
Debug.LogError($"[CardData] 未找到逻辑类 '{className}' 对应的脚本文件。\n" +
$"已搜索路径:\n{string.Join("\n", candidatePaths)}");
}
[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();
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(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();
var merged_original = new Dictionary();
// Collection 端字段名仍为 endowingCurrentAttributes(CardData 端已用 [FormerlySerializedAs] 改名)
var merged_runtime = new Dictionary();
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}'。");
}
// ── 内部共享辅助方法 ─────────────────────────────────────────────────
///
/// 通过 AssetDatabase 搜索特定类型/标签的资产,将文件名(无扩展名)作为下拉项返回。
/// 用于 References 列表的字符串引用选择。
///
private static IEnumerable> 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(name, name);
}
}
}
}
#endif