Files
Continentis/Assets/Scripts/MainGame/Base/EditorBaseCollection.cs
SoulliesOfficial ac98ec3aef 更新
2026-04-17 12:01:50 -04:00

338 lines
16 KiB
C#
Raw Permalink 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.
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如 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.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
}
}