338 lines
16 KiB
C#
338 lines
16 KiB
C#
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using Sirenix.OdinInspector;
|
||
using SLSUtilities.UModAssistance;
|
||
using SLSUtilities.General;
|
||
using UnityEngine;
|
||
|
||
#if UNITY_EDITOR
|
||
using Sirenix.OdinInspector.Editor;
|
||
#endif
|
||
|
||
namespace Continentis.MainGame.Base
|
||
{
|
||
[CreateAssetMenu(fileName = "EditorBaseCollection", menuName = "Continentis/BaseCollections/EditorBaseCollection")]
|
||
public partial class EditorBaseCollection : BaseCollection<EditorBaseCollection>
|
||
{
|
||
[Title("角色")]
|
||
[Tooltip("角色属性\nKey 为词条 ID(如 Health),Value 为词条中文描述(如 角色生命值)")]
|
||
[SerializedDictionarySettings("Key", "Description")]
|
||
public SerializedDictionary<string, string> characterGeneralAttributes = new SerializedDictionary<string, string>();
|
||
|
||
[Title("卡牌")]
|
||
[Tooltip("卡牌关键词\nKey 为词条 ID(如 Retain),Value 为词条中文描述(如 保留)")]
|
||
[SerializedDictionarySettings("Key", "Description")]
|
||
public SerializedDictionary<string, string> cardKeywords = new SerializedDictionary<string, string>();
|
||
|
||
[SerializedDictionarySettings("Key", "Description")]
|
||
[Tooltip("卡牌属性\nKey 为词条 ID(如 StaminaCost),Value 为词条中文描述(如 体力消耗)")]
|
||
public SerializedDictionary<string, string> cardAttributes = new SerializedDictionary<string, string>();
|
||
|
||
[SerializedDictionarySettings("Key", "Sprite")]
|
||
[Tooltip("意图图标\nKey 为意图 ID(如 Attack),Value 为对应的 Sprite 图标")]
|
||
public SerializedDictionary<string, Sprite> intentionIcons = new SerializedDictionary<string, Sprite>();
|
||
|
||
/// <summary>
|
||
/// 运行时:从 Resources 主包 + ModManager.Database 聚合所有已加载 Mod 的 EditorBaseCollection。
|
||
/// </summary>
|
||
public static IEnumerable<EditorBaseCollection> GetAllCollectionsRuntime()
|
||
{
|
||
// 主工程自带(放在 Resources 文件夹)
|
||
var main = Resources.Load<EditorBaseCollection>("EditorBaseCollection");
|
||
if (main != null) yield return main;
|
||
|
||
// 各 Mod 通过 ModManifest.SaveToDatabase 注册进来的
|
||
if (ModManager.Database.TryGetValue(typeof(EditorBaseCollection), out var dict))
|
||
{
|
||
foreach (var so in dict.Values)
|
||
{
|
||
if (so is EditorBaseCollection coll && coll != main)
|
||
yield return coll;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 运行时:按 key 从所有已加载 Mod 的 EditorBaseCollection 中查找意图图标 Sprite。
|
||
/// 主工程优先,Mod 按加载顺序兜底。未找到时返回 null。
|
||
/// </summary>
|
||
public static Sprite GetIntentionIcon(string key)
|
||
{
|
||
foreach (var coll in GetAllCollectionsRuntime())
|
||
{
|
||
if (coll.intentionIcons != null && coll.intentionIcons.TryGetValue(key, out Sprite sprite))
|
||
return sprite;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
/// <summary>
|
||
/// 提供给包含 CharacterAttributePair 的字典使用的下拉菜单获取方法
|
||
/// 支持全局扫描所有 Mod 的 EditorBaseCollection
|
||
/// </summary>
|
||
public static IEnumerable<ValueDropdownItem<string>> GetCharacterAttributesDropdown(InspectorProperty property)
|
||
{
|
||
List<ValueDropdownItem<string>> allItems = new List<ValueDropdownItem<string>>();
|
||
|
||
foreach (var coll in GetAllCollections())
|
||
{
|
||
if (coll.characterGeneralAttributes != null)
|
||
{
|
||
allItems.AddRange(coll.characterGeneralAttributes.Select(kvp => new ValueDropdownItem<string>
|
||
{
|
||
Text = $"{kvp.Key} ({kvp.Value})",
|
||
Value = kvp.Key
|
||
}));
|
||
}
|
||
}
|
||
|
||
// 去重(防止某些 Mod 覆盖/重复属性在展示时出现冗余项)
|
||
allItems = allItems.GroupBy(x => x.Value).Select(g => g.First()).ToList();
|
||
|
||
// 尝试排除已使用的 Key
|
||
try
|
||
{
|
||
object dict = SerializedDictionaryHelper.GetDictionaryFromKey(property);
|
||
if (dict is IEnumerable<KeyValuePair<string, float>> existingDict)
|
||
{
|
||
var usedKeys = existingDict.Select(k => k.Key).ToHashSet();
|
||
string currentKey = property.ValueEntry.WeakSmartValue as string;
|
||
// 显示未使用的项或者当前选中的项(即使已使用)
|
||
return allItems.Where(x => !usedKeys.Contains(x.Value) || x.Value == currentKey);
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 如果 SerializedDictionaryHelper 提取失败,就退化为显示全部,不打断使用
|
||
}
|
||
|
||
return allItems;
|
||
}
|
||
|
||
public static IEnumerable<EditorBaseCollection> GetAllCollections()
|
||
{
|
||
string[] guids = UnityEditor.AssetDatabase.FindAssets($"t:{nameof(EditorBaseCollection)}");
|
||
foreach (string guid in guids)
|
||
{
|
||
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
||
EditorBaseCollection coll = UnityEditor.AssetDatabase.LoadAssetAtPath<EditorBaseCollection>(path);
|
||
if (coll != null) yield return coll;
|
||
}
|
||
}
|
||
|
||
[BoxGroup("代码生成")]
|
||
[Tooltip("如果为空,则默认命名空间为 Continentis.MainGame;填写 Mod 简称则生成为 Continentis.Mods.<简称>")]
|
||
[LabelText("目标 Mod 简称 (如 Basic)")]
|
||
public string targetModName = "Basic";
|
||
|
||
[BoxGroup("代码生成")]
|
||
[Button("⚡ 生成 CharacterAttributes 静态脚本", ButtonSizes.Medium)]
|
||
[GUIColor(0.6f, 0.9f, 1f)]
|
||
public void GenerateAttributesScript()
|
||
{
|
||
string cleanModName = string.IsNullOrWhiteSpace(targetModName) ? string.Empty : targetModName.Trim();
|
||
string ns = string.IsNullOrEmpty(cleanModName) ? "Continentis.MainGame.Character" : $"Continentis.Mods.{cleanModName}.Character";
|
||
string className = string.IsNullOrEmpty(cleanModName) ? "CharacterAttributes" : $"{cleanModName}_CharacterAttributes";
|
||
|
||
string defaultPath = string.IsNullOrEmpty(cleanModName)
|
||
? Application.dataPath + "/Scripts/MainGame/Character/CharacterData"
|
||
: Application.dataPath + $"/Mods/{cleanModName}/Scripts";
|
||
|
||
if (!System.IO.Directory.Exists(defaultPath))
|
||
{
|
||
defaultPath = Application.dataPath;
|
||
}
|
||
|
||
string savePath = UnityEditor.EditorUtility.SaveFilePanel("保存生成的常量脚本", defaultPath, className + ".cs", "cs");
|
||
if (string.IsNullOrEmpty(savePath)) return;
|
||
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
if(!string.IsNullOrEmpty(cleanModName))
|
||
{
|
||
sb.AppendLine("using Continentis.MainGame;");
|
||
sb.AppendLine();
|
||
}
|
||
sb.AppendLine($"namespace {ns}");
|
||
sb.AppendLine("{");
|
||
sb.AppendLine(" /// <summary>");
|
||
sb.AppendLine($" /// 基于 {this.name} 自动生成的角色属性常量字典。");
|
||
sb.AppendLine(" /// 包含所有配置的 Key,以防止手写出现 Typo。");
|
||
sb.AppendLine(" /// </summary>");
|
||
sb.AppendLine(" [GameAttributeCollection]");
|
||
sb.AppendLine($" public static class {className}");
|
||
sb.AppendLine(" {");
|
||
|
||
// 生成属性
|
||
sb.AppendLine(" // ── 属性 (General) ───────────────────────────────");
|
||
if (characterGeneralAttributes != null)
|
||
{
|
||
foreach (var kvp in characterGeneralAttributes)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(kvp.Key)) continue;
|
||
if (!string.IsNullOrWhiteSpace(kvp.Value))
|
||
{
|
||
sb.AppendLine($" /// <summary> {kvp.Value} </summary>");
|
||
}
|
||
sb.AppendLine($" public const string {kvp.Key} = \"{kvp.Key}\";");
|
||
sb.AppendLine();
|
||
}
|
||
}
|
||
|
||
// 移除最后一个多余的空行
|
||
if (sb.Length >= 2) sb.Length -= 2;
|
||
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine("}");
|
||
|
||
System.IO.File.WriteAllText(savePath, sb.ToString(), System.Text.Encoding.UTF8);
|
||
UnityEditor.AssetDatabase.Refresh();
|
||
Debug.Log($"[代码生成] 已成功生成常量脚本:{savePath}");
|
||
}
|
||
|
||
[BoxGroup("代码生成")]
|
||
[Button("⚡ 生成 CardKeywords 静态脚本", ButtonSizes.Medium)]
|
||
[GUIColor(0.6f, 1f, 0.8f)]
|
||
public void GenerateCardKeywordsScript()
|
||
{
|
||
string cleanModName = string.IsNullOrWhiteSpace(targetModName) ? string.Empty : targetModName.Trim();
|
||
string ns = string.IsNullOrEmpty(cleanModName) ? "Continentis.MainGame.Card" : $"Continentis.Mods.{cleanModName}.Card";
|
||
string className = string.IsNullOrEmpty(cleanModName) ? "CardKeywords" : $"{cleanModName}_CardKeywords";
|
||
|
||
string defaultPath = string.IsNullOrEmpty(cleanModName)
|
||
? Application.dataPath + "/Scripts/MainGame/Card"
|
||
: Application.dataPath + $"/Mods/{cleanModName}/Scripts";
|
||
|
||
if (!System.IO.Directory.Exists(defaultPath)) defaultPath = Application.dataPath;
|
||
|
||
string savePath = UnityEditor.EditorUtility.SaveFilePanel("保存生成的关键词脚本", defaultPath, className + ".cs", "cs");
|
||
if (string.IsNullOrEmpty(savePath)) return;
|
||
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
sb.AppendLine($"namespace {ns}");
|
||
sb.AppendLine("{");
|
||
sb.AppendLine(" /// <summary>");
|
||
sb.AppendLine($" /// 基于 {this.name} 自动生成的卡牌关键词常量字典。");
|
||
sb.AppendLine(" /// 包含所有配置的 Key,以防止手写出现 Typo。");
|
||
sb.AppendLine(" /// </summary>");
|
||
sb.AppendLine($" public static class {className}");
|
||
sb.AppendLine(" {");
|
||
|
||
if (cardKeywords != null)
|
||
{
|
||
foreach (var kvp in cardKeywords)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(kvp.Key)) continue;
|
||
if (!string.IsNullOrWhiteSpace(kvp.Value))
|
||
sb.AppendLine($" /// <summary> {kvp.Value} </summary>");
|
||
sb.AppendLine($" public const string {kvp.Key} = \"{kvp.Key}\";");
|
||
sb.AppendLine();
|
||
}
|
||
}
|
||
|
||
if (sb.Length >= 2) sb.Length -= 2;
|
||
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine("}");
|
||
|
||
System.IO.File.WriteAllText(savePath, sb.ToString(), System.Text.Encoding.UTF8);
|
||
UnityEditor.AssetDatabase.Refresh();
|
||
Debug.Log($"[代码生成] 已成功生成关键词脚本:{savePath}");
|
||
}
|
||
|
||
[BoxGroup("代码生成")]
|
||
[Button("⚡ 生成 CardAttributes 静态脚本", ButtonSizes.Medium)]
|
||
[GUIColor(0.6f, 1f, 0.8f)]
|
||
public void GenerateCardAttributesScript()
|
||
{
|
||
string cleanModName = string.IsNullOrWhiteSpace(targetModName) ? string.Empty : targetModName.Trim();
|
||
string ns = string.IsNullOrEmpty(cleanModName) ? "Continentis.MainGame.Card" : $"Continentis.Mods.{cleanModName}.Card";
|
||
string className = string.IsNullOrEmpty(cleanModName) ? "CardAttributes" : $"{cleanModName}_CardAttributes";
|
||
|
||
string defaultPath = string.IsNullOrEmpty(cleanModName)
|
||
? Application.dataPath + "/Scripts/MainGame/Card"
|
||
: Application.dataPath + $"/Mods/{cleanModName}/Scripts";
|
||
|
||
if (!System.IO.Directory.Exists(defaultPath)) defaultPath = Application.dataPath;
|
||
|
||
string savePath = UnityEditor.EditorUtility.SaveFilePanel("保存生成的卡牌属性脚本", defaultPath, className + ".cs", "cs");
|
||
if (string.IsNullOrEmpty(savePath)) return;
|
||
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
sb.AppendLine($"namespace {ns}");
|
||
sb.AppendLine("{");
|
||
sb.AppendLine(" /// <summary>");
|
||
sb.AppendLine($" /// 基于 {this.name} 自动生成的卡牌属性常量字典。");
|
||
sb.AppendLine(" /// 包含所有配置的 Key,以防止手写出现 Typo。");
|
||
sb.AppendLine(" /// </summary>");
|
||
sb.AppendLine($" public static class {className}");
|
||
sb.AppendLine(" {");
|
||
|
||
if (cardAttributes != null)
|
||
{
|
||
foreach (var kvp in cardAttributes)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(kvp.Key)) continue;
|
||
if (!string.IsNullOrWhiteSpace(kvp.Value))
|
||
sb.AppendLine($" /// <summary> {kvp.Value} </summary>");
|
||
sb.AppendLine($" public const string {kvp.Key} = \"{kvp.Key}\";");
|
||
sb.AppendLine();
|
||
}
|
||
}
|
||
|
||
if (sb.Length >= 2) sb.Length -= 2;
|
||
|
||
sb.AppendLine(" }");
|
||
sb.AppendLine("}");
|
||
|
||
System.IO.File.WriteAllText(savePath, sb.ToString(), System.Text.Encoding.UTF8);
|
||
UnityEditor.AssetDatabase.Refresh();
|
||
Debug.Log($"[代码生成] 已成功生成卡牌属性脚本:{savePath}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提供给 CardData keywords 列表使用的关键词下拉菜单。
|
||
/// 聚合所有 Mod 的 EditorBaseCollection 中的 cardKeywords 字典。
|
||
/// </summary>
|
||
public static IEnumerable<ValueDropdownItem<string>> GetCardKeywordsDropdown()
|
||
{
|
||
HashSet<string> seen = new HashSet<string>(System.StringComparer.Ordinal);
|
||
foreach (var coll in GetAllCollections())
|
||
{
|
||
if (coll.cardKeywords == null) continue;
|
||
foreach (var kvp in coll.cardKeywords)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(kvp.Key)) continue;
|
||
if (seen.Add(kvp.Key))
|
||
yield return new ValueDropdownItem<string>($"{kvp.Key} ({kvp.Value})", kvp.Key);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提供给 CardData 属性字典使用的属性名下拉菜单。
|
||
/// 聚合所有 Mod 的 EditorBaseCollection 中的 cardAttributes 字典。
|
||
/// </summary>
|
||
public static IEnumerable<ValueDropdownItem<string>> GetCardAttributesDropdown()
|
||
{
|
||
HashSet<string> seen = new HashSet<string>(System.StringComparer.Ordinal);
|
||
foreach (var coll in GetAllCollections())
|
||
{
|
||
if (coll.cardAttributes == null) continue;
|
||
foreach (var kvp in coll.cardAttributes)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(kvp.Key)) continue;
|
||
if (seen.Add(kvp.Key))
|
||
yield return new ValueDropdownItem<string>($"{kvp.Key} ({kvp.Value})", kvp.Key);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OpenAttributeGenerator()
|
||
{
|
||
GenerateAttributesScript();
|
||
}
|
||
#endif
|
||
}
|
||
}
|