Files
Continentis/Assets/Scripts/MainGame/Base/EditorBaseCollection.cs

367 lines
17 KiB
C#
Raw Normal View History

2026-03-20 11:56:50 -04:00
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using SLSFramework.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如 StrengthValue 为词条中文描述(如 力量)")]
[SerializedDictionarySettings("Key", "Description")]
public SerializedDictionary<string, string> characterCoreAttributes = new SerializedDictionary<string, string>();
[Tooltip("通用与状态属性\nKey 为词条 ID如 HealthValue 为词条中文描述(如 角色生命值)")]
[SerializedDictionarySettings("Key", "Description")]
public SerializedDictionary<string, string> characterGeneralAttributes = new SerializedDictionary<string, string>();
[Title("卡牌")]
[Tooltip("卡牌关键词\nKey 为词条 ID如 RetainValue 为词条中文描述(如 保留)")]
[SerializedDictionarySettings("Key", "Description")]
public SerializedDictionary<string, string> cardKeywords = new SerializedDictionary<string, string>();
[SerializedDictionarySettings("Key", "Description")]
[Tooltip("卡牌属性\nKey 为词条 ID如 StaminaCostValue 为词条中文描述(如 体力消耗)")]
public SerializedDictionary<string, string> cardAttributes = new SerializedDictionary<string, string>();
[SerializedDictionarySettings("Key", "Sprite")]
[Tooltip("意图图标\nKey 为意图 ID如 AttackValue 为对应的 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.characterCoreAttributes != null)
{
allItems.AddRange(coll.characterCoreAttributes.Select(kvp => new ValueDropdownItem<string>
{
Text = $"{kvp.Key} ({kvp.Value})",
Value = kvp.Key
}));
}
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(" // ── 核心属性 (Core) ──────────────────────────────────");
if (characterCoreAttributes != null)
{
foreach (var kvp in characterCoreAttributes)
{
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();
}
}
// 生成通用属性
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
}
}