using System;
using System.Collections.Generic;
using System.Linq;
using Sirenix.OdinInspector;
using UnityEngine;
namespace Cielonos.MainGame.Inventory
{
///
/// 存储连招树定义的 ScriptableObject。
/// 这是你的“蓝图”资产。
///
[CreateAssetMenu(fileName = "ComboData", menuName = "Cielonos/Items/ComboData")]
public class ComboData : ScriptableObject
{
public float defaultDisappearTime;
[InlineProperty] [HideLabel] public ComboTreeData tree;
[OnInspectorInit]
private void OnEnable()
{
defaultDisappearTime = 0.25f;
tree ??= new ComboTreeData();
// 确保总有一个 RootNode
if (tree.nodes == null || tree.nodes.Count == 0)
{
tree.nodes = new List();
// 添加不可删除的 RootNode
tree.nodes.Add(new ComboNodeData() { referenceName = "Root" });
}
// 关键一步:将树实例传递给所有子节点,以便它们能填充下拉列表
tree.InitializeNodeReferences();
}
}
///
/// 连招树的数据容器
///
[Serializable]
public class ComboTreeData
{
// --- 修改点 ---
// 将标题移到这里,使其在内联属性的顶部显示
[Title("连招树编辑器", "可视化编辑连招树的节点和分支")]
// --- 修改结束 ---
[ListDrawerSettings(
DraggableItems = true, // 现在可以安全地拖动排序了!
NumberOfItemsPerPage = 20,
CustomAddFunction = "AddNode",
CustomRemoveElementFunction = "RemoveNode")]
public List nodes;
///
/// Odin Inspector 自定义添加按钮的实现
///
private void AddNode()
{
// 查找一个唯一的默认名称
int counter = nodes.Count - 1;
string newName = counter.ToString();
while (nodes.Any(n => n.referenceName == newName))
{
counter++;
newName = counter.ToString();
}
var newNode = new ComboNodeData() { referenceName = newName };
nodes.Add(newNode);
// 添加后,立即初始化引用
InitializeNodeReferences();
}
///
/// Odin Inspector 自定义移除按钮的实现
///
private void RemoveNode(ComboNodeData node)
{
// 规则:不可删除 RootNode
if (node.referenceName == "Root")
{
Debug.LogWarning("不能删除 Root 节点。");
return;
}
nodes.Remove(node);
InitializeNodeReferences(); // 移除后,更新引用
}
///
/// 将父树实例传递给所有节点(和分支),用于Odin下拉列表
///
public void InitializeNodeReferences()
{
if (nodes == null) return;
}
///
/// 帮助 Odin 下拉列表获取所有可用节点名称
///
public IEnumerable GetNodeReferenceNames()
{
if (nodes == null) return Enumerable.Empty();
return nodes.Select(n => n.referenceName).Where(n => !string.IsNullOrEmpty(n));
}
public ComboSubmodule.ComboTree ToRuntime()
{
var runtimeTree = new ComboSubmodule.ComboTree();
runtimeTree.nodes = new List();
// 创建运行时节点
foreach (var nodeData in nodes)
{
var runtimeNode = new ComboSubmodule.ComboTree.Node(nodes.IndexOf(nodeData), nodeData.referenceName);
runtimeNode.branches = new List();
// 创建运行时分支
foreach (var branchData in nodeData.branches)
{
var runtimeBranch = new ComboSubmodule.ComboTree.Branch(nodes.FindIndex(n => n.referenceName == branchData.nextNodeRefName), branchData.operation);
runtimeNode.branches.Add(runtimeBranch);
}
runtimeTree.nodes.Add(runtimeNode);
}
return runtimeTree;
}
}
///
/// 连招节点的数据定义
///
[Serializable]
public class ComboNodeData
{
[ValidateInput("ValidateReferenceName", "引用名称(Reference Name)必须唯一且不能为空。")]
[InfoBox("这是 Root 节点,是所有连招的起点。", InfoMessageType.Info, "IsRootNode")]
public string referenceName;
[ListDrawerSettings(NumberOfItemsPerPage = 5)]
public List branches = new List();
private bool IsRootNode() => referenceName == "Root";
private bool ValidateReferenceName(string name, ref string errorMessage)
{
if (string.IsNullOrEmpty(name))
{
errorMessage = "引用名称不能为空。";
return false;
}
return true;
}
}
///
/// 连招分支的数据定义
///
[Serializable]
public class ComboBranchData
{
[Tooltip("触发此分支的输入操作,例如 'L', 'R', 'Attack'")] [HorizontalGroup("Branch", 100)] [LabelWidth(60)]
public string operation;
[Tooltip("此分支链接到的下一个节点")]
[HorizontalGroup("Branch")]
[LabelText("下一个节点")]
public string nextNodeRefName; // <-- 使用名称引用,而不是索引
// --- Odin 辅助字段和方法 ---
}
}