架构大更

This commit is contained in:
SoulliesOfficial
2026-03-20 11:56:50 -04:00
parent e60ef64d01
commit d09b58fd80
3663 changed files with 15232012 additions and 105579 deletions

View File

@@ -0,0 +1,81 @@
using System;
using Sirenix.OdinInspector;
using SLSUtilities.General;
using UnityEngine;
namespace Continentis.MainGame.Character
{
/// <summary>
/// 使用新版 SerializedDictionary 和 CharacterAttributePair 的全新配置集合类。
/// (目前不替换旧的 CharacterAttributesDefaultCollection请按需测试本版本
/// </summary>
[CreateAssetMenu(fileName = "CharacterAttributeCollection",
menuName = "Continentis/MainGame/Character/CharacterAttributeCollection", order = 1)]
public class CharacterAttributeCollection : ScriptableObject
{
[Title("Attributes新特性架构测试版")] [Tooltip("角色的通常属性:支持下拉菜单/手写自由切换的优雅 UI 交互!")]
public SerializedDictionary<string, float, CharacterAttributePair> originalAttributes;
[Tooltip("初始化时赋予给CurrentAttributes的属性\n" +
"留空则默认为0填数字则使用数字。")]
public SerializedDictionary<string, string> runtimeAttributes;
}
/// <summary>
/// 自定义的带有极简美学 UI 的字典序列化结构体。
/// 可以同时包含“下拉菜单安全输入”以及“纯手写高级输入”两种状态。
/// </summary>
[Serializable]
public struct CharacterAttributePair : ISerializedPair<string, float>
{
[SerializeField] [HideInInspector] public string attributeKey;
[SerializeField] [HideInInspector] private bool useManualInput;
[ShowInInspector]
[PropertyOrder(0)]
[HideIf("useManualInput")]
[HorizontalGroup("H")]
[HideLabel]
[ValueDropdown("@Continentis.MainGame.Base.EditorBaseCollection.GetCharacterAttributesDropdown($property)",
IsUniqueList = true, DropdownHeight = 400)]
[InlineButton("ToggleMode", Icon = SdfIconType.ListUl, Label = "")]
public string DropdownKey
{
get => attributeKey;
set => attributeKey = value;
}
[ShowInInspector]
[PropertyOrder(0)]
[ShowIf("useManualInput")]
[HorizontalGroup("H")]
[HideLabel]
[InlineButton("ToggleMode", Icon = SdfIconType.PencilSquare, Label = "")]
public string ManualKey
{
get => attributeKey;
set => attributeKey = value;
}
[PropertyOrder(10)] [HorizontalGroup("H", MarginLeft = 10, Width = 100)] [HideLabel]
public float attributeValue;
// 供 SLSUtilities.General 的接口实现
public string Key
{
get => attributeKey;
set => attributeKey = value;
}
public float Value
{
get => attributeValue;
set => attributeValue = value;
}
private void ToggleMode()
{
useManualInput = !useManualInput;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9de2fe8a62ebef640ae5ce8697f0f5b0

View File

@@ -1,24 +1,34 @@
using System.Collections.Generic;
using NaughtyAttributes;
using SLSFramework.General;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
namespace Continentis.MainGame.Character
{
[CreateAssetMenu(menuName = "Continentis/MainGame/Character/AttributesDefaultCollection", fileName = "CharacterAttributesDefaultCollection")]
/// <summary>
/// 角色属性默认值模板。
/// 放置于 Assets/Mods/{ModName}/Characters/DefaultCollections/ 路径下才会被 PasteDefaultAttributes 识别并导入。
/// </summary>
[CreateAssetMenu(menuName = "Continentis/MainGame/Character/AttributesDefaultCollection",
fileName = "CharacterAttributesDefaultCollection")]
public class CharacterAttributesDefaultCollection : ScriptableObject
{
[Header("Attributes")]
[KeyWidth(0.75f)]
[TitleGroup("属性模板")]
[DictionaryDrawerSettings(KeyLabel = "属性名", ValueLabel = "属性值")]
[LabelText("核心属性 (Core)")]
[Tooltip("对应 CharacterData.coreAttributes")]
public SerializableDictionary<string, float> coreAttributes;
[KeyWidth(0.75f)]
[DictionaryDrawerSettings(KeyLabel = "属性名", ValueLabel = "属性值")]
[LabelText("通常属性 (General)")]
[Tooltip("对应 CharacterData.generalAttributes")]
public SerializableDictionary<string, float> generalAttributes;
[FormerlySerializedAs("endowingGeneralAttributes")]
[KeyWidth(0.5f)]
[Tooltip("初始化时赋予给CurrentGeneralAttributes的属性第一栏是属性名第二栏是初始化时使用对应名称的GeneralAttributes的数据或一个常数留空则默认为0")]
[DictionaryDrawerSettings(KeyLabel = "属性名", ValueLabel = "初始值表达式")]
[LabelText("运行时通常属性 (Runtime General)")]
[Tooltip("初始化时赋予给 CurrentGeneralAttributes 的属性Value 填写对应 GeneralAttributes 的 Key 名(或一个常数),留空则默认为 0。")]
public SerializableDictionary<string, string> runtimeGeneralAttributes;
}
}

View File

@@ -1,129 +1,151 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Continentis.MainGame.Card;
using Continentis.MainGame.UI;
using NaughtyAttributes;
using SLSFramework.General;
using Sirenix.OdinInspector;
using SLSFramework.UModAssistance;
using SLSUtilities.General;
using UnityEngine;
using UnityEngine.Serialization;
#if UNITY_EDITOR
using System.IO;
using UnityEditor;
#endif
namespace Continentis.MainGame.Character
{
// ─────────────────────────────────────────────────────────────────────────
// CharacterData — ScriptableObject 数据定义
// ─────────────────────────────────────────────────────────────────────────
[CreateAssetMenu(menuName = "Continentis/MainGame/Character/CharacterData", fileName = "CharacterData")]
public partial class CharacterData : ScriptableObject
{
[Header("Fundamental")]
// ── Fundamental ───────────────────────────────────────────────────────
[BoxGroup("Fundamental"), PropertyOrder(0)]
[LabelText("使用自定义逻辑类")]
[Tooltip("勾选后可通过下拉菜单选择一个 CharacterLogicBase 子类,并自动填充 modName / className。")]
public bool haveCustomClass;
// haveCustomClass = true 时:逻辑类选择器(替代旧 DrawSearchableTypeSelector
[BoxGroup("Fundamental"), PropertyOrder(1)]
[ShowIf("haveCustomClass")]
[InfoBox("请通过下拉菜单选择角色逻辑类Mod 名 / 类名将自动填充。", InfoMessageType.None)]
[ValueDropdown("@CharacterData.GetCharacterLogicDropdownItems()", AppendNextDrawer = true, DisableGUIInAppendedDrawer = true)]
[LabelText("逻辑类全名"), OnValueChanged("OnCharacterLogicClassSelected")]
public string classFullName;
// haveCustomClass = false 时:手动填写 modName / className
[BoxGroup("Fundamental"), PropertyOrder(2)]
[HideIf("haveCustomClass")]
[LabelText("Mod 名称")]
public string modName;
[BoxGroup("Fundamental"), PropertyOrder(3)]
[HideIf("haveCustomClass")]
[LabelText("类名称")]
public string className;
[BoxGroup("Fundamental"), PropertyOrder(4)]
[LabelText("显示名称 Key")]
public string displayName;
[BoxGroup("Fundamental"), PropertyOrder(5)]
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = true)]
[LabelText("标签")]
public List<string> tags;
// ── 立绘与描述 ────────────────────────────────────────────────────────
[BoxGroup("Visuals"), PropertyOrder(6)]
[PreviewField(80, ObjectFieldAlignment.Left), LabelText("头像 (Avatar)")]
public Sprite avatar;
[BoxGroup("Visuals"), PropertyOrder(7)]
[PreviewField(120, ObjectFieldAlignment.Left), LabelText("立绘 (Portrait)")]
public Sprite portrait;
[BoxGroup("Visuals"), PropertyOrder(8)]
[LabelText("角色描述 Key")]
public string characterDescription;
[BoxGroup("Visuals"), PropertyOrder(9)]
[LabelText("角色故事 Key")]
public string characterStory;
[Header("Attributes")]
[Tooltip("角色的核心属性:第一栏是属性名,第二栏是属性值")]
public SerializableDictionary<string, float> coreAttributes;
[Tooltip("角色的通常属性:第一栏是属性名,第二栏是属性值")]
public SerializableDictionary<string, float> generalAttributes;
[Tooltip("初始化时赋予给CurrentGeneralAttributes的属性第一栏是属性名第二栏是初始化时使用对应名称的GeneralAttributes的数据留空则默认为0,如果是float数字则直接使用该数字")]
public SerializableDictionary<string, string> runtimeGeneralAttributes;
[Header("View")]
// ── Attributes ────────────────────────────────────────────────────────
[TabGroup("Data", "属性"), PropertyOrder(20)]
[Tooltip("角色的核心属性:支持智能下拉菜单")]
public SerializedDictionary<string, float, CharacterAttributePair> coreAttributes = new SerializedDictionary<string, float, CharacterAttributePair>();
[TabGroup("Data", "属性"), PropertyOrder(21)]
[Tooltip("角色的通常属性:支持智能下拉菜单")]
public SerializedDictionary<string, float, CharacterAttributePair> generalAttributes = new SerializedDictionary<string, float, CharacterAttributePair>();
[TabGroup("Data", "属性"), PropertyOrder(22)]
[DictionaryDrawerSettings(KeyLabel = "运行时状态槽 (如 Health)", ValueLabel = "绑定的最大值标记 (如 MaximumHealth)")]
[Tooltip("初始化时赋予给 CurrentGeneralAttributes 的属性Value 填写对应 GeneralAttributes 的 Key 名,或直接填浮点数,留空默认为 0。")]
public SerializedDictionary<string, string> runtimeGeneralAttributes = new SerializedDictionary<string, string>();
// ── View ──────────────────────────────────────────────────────────────
[TabGroup("Data", "视图"), PropertyOrder(30)]
[LabelText("战斗位置顺序")]
[MinValue(0)]
public int combatPositionOrder;
[TabGroup("Data", "视图"), PropertyOrder(31)]
[LabelText("战斗角色视图 Prefab")]
public GameObject combatCharacterView;
[TabGroup("Data", "视图"), PropertyOrder(32)]
[DictionaryDrawerSettings(KeyLabel = "动画名称", ValueLabel = "AnimationClip")]
[LabelText("动画映射")]
public SerializableDictionary<string, AnimationClip> animations;
[Header("References")]
// ── References ────────────────────────────────────────────────────────
[TabGroup("Data", "引用"), PropertyOrder(40)]
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
[ValueDropdown("GetAvailablePrefabs", AppendNextDrawer = true)]
[LabelText("Prefab 引用")]
public List<string> prefabRefs = new List<string>();
[TabGroup("Data", "引用"), PropertyOrder(41)]
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
[ValueDropdown("GetAvailableCardData", AppendNextDrawer = true)]
[LabelText("衍生卡牌数据引用")]
public List<string> derivativeCardDataRefs = new List<string>();
[TabGroup("Data", "引用"), PropertyOrder(42)]
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
[ValueDropdown("GetAvailableCharacterData", AppendNextDrawer = true)]
[LabelText("衍生角色数据引用")]
public List<string> derivativeCharacterDataRefs = new List<string>();
[TabGroup("Data", "引用"), PropertyOrder(43)]
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
[ValueDropdown("GetAvailableCardData", AppendNextDrawer = true)]
[Tooltip("初始卡组引用列表")]
[LabelText("初始卡组引用")]
public List<string> initialDeckRef;
[Tooltip("HUD信息引用列表")]
[TabGroup("Data", "引用"), PropertyOrder(44)]
[ListDrawerSettings(ShowIndexLabels = false, DraggableItems = false)]
[ValueDropdown("GetAvailableHUDData", AppendNextDrawer = true)]
[Tooltip("HUD 信息引用列表")]
[LabelText("HUD 数据引用")]
public List<string> hudDataRefs;
}
// ─────────────────────────────────────────────────────────────────────────
// Runtime: 数据库查询
// ─────────────────────────────────────────────────────────────────────────
public partial class CharacterData
{
/// <summary>
/// 通过 DataID 从 ModManager 数据库查找 CharacterData。
/// </summary>
public static CharacterData Get(string dataID)
{
return ModManager.GetData<CharacterData>(dataID);
}
}
#if UNITY_EDITOR
public partial class CharacterData
{
public void PasteDefaultAttributes()
{
List<CharacterAttributesDefaultCollection> targetCollections = new List<CharacterAttributesDefaultCollection>();
string[] guids = AssetDatabase.FindAssets("t:CharacterAttributesDefaultCollection");
if (guids.Length > 1)
{
Debug.Log("找到了多个CharacterAttributesDefaultCollection它们将被合并后导入CharacterData");
}
foreach (string guid in guids)
{
// 将GUID转换为资产的路径
string path = AssetDatabase.GUIDToAssetPath(guid);
// 2. 验证每个资产的路径是否完全符合您指定的结构
// 使用 Path.GetDirectoryName 获取文件所在的目录
// 并统一使用'/'作为路径分隔符,以兼容不同操作系统
string directory = Path.GetDirectoryName(path)?.Replace('\\', '/');
Debug.Log($"Checking asset at path: {path}, directory: {directory}");
// 加载资产以检查其命名空间
ScriptableObject collection = AssetDatabase.LoadAssetAtPath<ScriptableObject>(path);
Type assetType = collection.GetType();
string assetNamespace = assetType.Namespace;
// 检查目录是否有效,是否在"Assets/Mods/"下,并且是否以"/Characters/DefaultCollections"结尾
if (!string.IsNullOrEmpty(directory) &&
assetNamespace == typeof(CharacterAttributesDefaultCollection).Namespace &&
directory.StartsWith("Assets/Mods/") &&
directory.EndsWith("/Characters/DefaultCollections"))
{
// 3. 如果路径和命名空间都符合要求,则将该资产添加到目标列表中
CharacterAttributesDefaultCollection defaultList = collection as CharacterAttributesDefaultCollection;
targetCollections.Add(defaultList);
Debug.Log($"Loaded DefaultStringList from: {path}");
}
}
Dictionary<string, float> coreAttributes = new Dictionary<string, float>();
Dictionary<string, float> generalAttributes = new Dictionary<string, float>();
Dictionary<string, string> runtimeGeneralAttributes = new Dictionary<string, string>();
foreach (CharacterAttributesDefaultCollection collection in targetCollections)
{
collection.coreAttributes.PasteDictionary(coreAttributes);
collection.generalAttributes.PasteDictionary(generalAttributes);
collection.runtimeGeneralAttributes.PasteDictionary(runtimeGeneralAttributes);
}
coreAttributes.PasteDictionary(this.coreAttributes);
generalAttributes.PasteDictionary(this.generalAttributes);
runtimeGeneralAttributes.PasteDictionary(this.runtimeGeneralAttributes);
Debug.Log($"Pasted default attributes to file {this.name}");
}
}
#endif
}