Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Managers/SkillTreeManager.cs

333 lines
14 KiB
C#
Raw 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 Data;
using System.Collections.Generic;
using System.Linq;
namespace Managers
{
public class SkillTreeManager : Utils.Singleton<SkillTreeManager>, ILaunchManager
{
public string StepDescription => "正在加载技能树";
// 内部数据结构,用于高效查询和图遍历
private Dictionary<string, SkillTreeDef> _skillNodesByDefName;
private Dictionary<string, List<SkillTreeDef>> _skillNodesByTag;
private Dictionary<string, List<SkillTreeDef>> _skillNodesByFaction;
private Dictionary<string, List<SkillTreeDef>> _childrenOfNode; // key: parent defName, value: list of children nodes
public void Init()
{
// 假设 DefineManager.Instance 存在且 QueryDefinesByType 方法可用
// 如果 DefineManager 是单例,这里直接调用即可。
// 否则,需要确保 DefineManager 在此之前已被正确初始化。
var allSkillTreeNodes = DefineManager.Instance.QueryDefinesByType<SkillTreeDef>();
// 1. 初始化字典
_skillNodesByDefName = new Dictionary<string, SkillTreeDef>();
_skillNodesByTag = new Dictionary<string, List<SkillTreeDef>>();
_skillNodesByFaction = new Dictionary<string, List<SkillTreeDef>>();
_childrenOfNode = new Dictionary<string, List<SkillTreeDef>>();
// 2. 填充基本查询字典
foreach (var node in allSkillTreeNodes)
{
if (string.IsNullOrEmpty(node.defName))
{
// 可以选择抛出异常或记录警告,取决于项目的错误处理策略
// 例如: Log.Warning($"SkillTreeDef with empty defName found. Skipping.");
continue;
}
if (!_skillNodesByDefName.TryAdd(node.defName, node))
{
// 避免重复defName通常defName应该是唯一的
// 例如: Log.Error($"Duplicate SkillTreeDef defName '{node.defName}' found. Only the first one will be used.");
continue; // 跳过此重复节点
}
// 填充 _skillNodesByTag
if (!string.IsNullOrEmpty(node.tag))
{
if (!_skillNodesByTag.ContainsKey(node.tag))
{
_skillNodesByTag[node.tag] = new List<SkillTreeDef>();
}
_skillNodesByTag[node.tag].Add(node);
}
// 填充 _skillNodesByFaction
// 假设 AffiliationDef 也有 defName 属性作为唯一标识
if (node.faction != null && !string.IsNullOrEmpty(node.faction.defName))
{
if (!_skillNodesByFaction.ContainsKey(node.faction.defName))
{
_skillNodesByFaction[node.faction.defName] = new List<SkillTreeDef>();
}
_skillNodesByFaction[node.faction.defName].Add(node);
}
}
// 3. 填充 _childrenOfNode 字典(构建父 -> 子关系)
// 需要再次遍历,因为在填充 _skillNodesByDefName 之前prerequisites 中的引用可能尚未完全解析。
foreach (var childNode in allSkillTreeNodes)
{
if (childNode.prerequisites != null)
{
foreach (var prerequisite in childNode.prerequisites)
{
// 确保父节点在 _skillNodesByDefName 中存在,避免野指针或无效引用
if (prerequisite != null && _skillNodesByDefName.TryGetValue(prerequisite.defName, out var parentNode))
{
if (!_childrenOfNode.ContainsKey(parentNode.defName))
{
_childrenOfNode[parentNode.defName] = new List<SkillTreeDef>();
}
_childrenOfNode[parentNode.defName].Add(childNode);
}
else if (prerequisite != null)
{
// 记录警告prerequisites中引用的父节点找不到定义可能是数据配置错误
// 例如: Log.Warning($"SkillTreeDef '{childNode.defName}' references unknown prerequisite '{prerequisite.defName}'.");
}
}
}
}
}
public void Clear()
{
// 清理所有数据结构,释放内存
_skillNodesByDefName?.Clear();
_skillNodesByTag?.Clear();
_skillNodesByFaction?.Clear();
_childrenOfNode?.Clear();
// 将引用设为null有助于GC
_skillNodesByDefName = null;
_skillNodesByTag = null;
_skillNodesByFaction = null;
_childrenOfNode = null;
}
// --- 节点查询 ---
/// <summary>
/// 根据定义名查询技能树节点
/// </summary>
/// <param name="defName">技能树节点的定义名</param>
/// <returns>匹配的 SkillTreeDef如果未找到则为 null</returns>
public SkillTreeDef GetNodeByDefName(string defName)
{
if (string.IsNullOrEmpty(defName)) return null;
_skillNodesByDefName.TryGetValue(defName, out var node);
return node;
}
/// <summary>
/// 根据标签查询技能树节点列表
/// </summary>
/// <param name="tag">技能树节点的标签</param>
/// <returns>匹配的 SkillTreeDef 列表,如果未找到则返回空列表</returns>
public List<SkillTreeDef> GetNodesByTag(string tag)
{
if (string.IsNullOrEmpty(tag)) return new List<SkillTreeDef>();
_skillNodesByTag.TryGetValue(tag, out var nodes);
return nodes ?? new List<SkillTreeDef>();
}
/// <summary>
/// 根据派系名称查询技能树节点列表
/// </summary>
/// <param name="factionName">派系的定义名</param>
/// <returns>匹配的 SkillTreeDef 列表,如果未找到则返回空列表</returns>
public List<SkillTreeDef> GetNodesByFaction(string factionName)
{
if (string.IsNullOrEmpty(factionName)) return new List<SkillTreeDef>();
_skillNodesByFaction.TryGetValue(factionName, out var nodes);
return nodes ?? new List<SkillTreeDef>();
}
public string[] GetAllTag()
{
return _skillNodesByTag.Keys.ToArray();
}
// --- 父子关系查询 ---
/// <summary>
/// 判断 targetNode 是否是 baseNode 的直接父节点
/// </summary>
/// <param name="baseNode">作为父节点候选的节点</param>
/// <param name="targetNode">作为子节点候选的节点</param>
/// <returns>如果是直接父节点则为 true否则为 false</returns>
public bool IsDirectParentOf(SkillTreeDef baseNode, SkillTreeDef targetNode)
{
if (baseNode == null || targetNode == null || targetNode.prerequisites == null) return false;
return targetNode.prerequisites.Any(prerequisite => prerequisite != null && prerequisite.defName == baseNode.defName);
}
/// <summary>
/// 判断 targetNode 是否是 baseNode 的直接子节点
/// </summary>
/// <param name="baseNode">作为父节点的节点</param>
/// <param name="targetNode">作为子节点候选的节点</param>
/// <returns>如果是直接子节点则为 true否则为 false</returns>
public bool IsDirectChildOf(SkillTreeDef baseNode, SkillTreeDef targetNode)
{
if (baseNode == null || targetNode == null) return false;
// 查找 baseNode 的子节点列表中是否包含 targetNode
_childrenOfNode.TryGetValue(baseNode.defName, out var children);
return children != null && children.Any(c => c.defName == targetNode.defName);
}
/// <summary>
/// 判断 ancestor 是否是 descendant 的任何一级父节点(包括直接和间接)
/// </summary>
/// <param name="ancestor">祖先节点候选</param>
/// <param name="descendant">后代节点候选</param>
/// <returns>如果是祖先节点则为 true否则为 false</returns>
public bool IsAncestorOf(SkillTreeDef ancestor, SkillTreeDef descendant)
{
if (ancestor == null || descendant == null) return false;
if (ancestor.defName == descendant.defName) return false; // 自己不是自己的祖先
return GetAllAncestors(descendant).Any(n => n.defName == ancestor.defName);
}
/// <summary>
/// 判断 descendant 是否是 ancestor 的任何一级子节点(包括直接和间接)
/// </summary>
/// <param name="descendant">后代节点候选</param>
/// <param name="ancestor">祖先节点候选</param>
/// <returns>如果是后代节点则为 true否则为 false</returns>
public bool IsDescendantOf(SkillTreeDef descendant, SkillTreeDef ancestor)
{
if (descendant == null || ancestor == null) return false;
if (descendant.defName == ancestor.defName) return false; // 自己不是自己的后代
return GetAllDescendants(ancestor).Any(n => n.defName == descendant.defName);
}
// --- 获取所有相关节点 ---
/// <summary>
/// 获取所有直接父节点
/// </summary>
/// <param name="node">要查询的节点</param>
/// <returns>直接父节点列表,如果无父节点则返回空列表</returns>
public List<SkillTreeDef> GetAllDirectParents(SkillTreeDef node)
{
if (node == null || node.prerequisites == null) return new List<SkillTreeDef>();
// 确保返回的是真实存在于管理器中的节点,而不是可能无效的引用
return node.prerequisites.Where(p => p != null && _skillNodesByDefName.ContainsKey(p.defName))
.Select(p => _skillNodesByDefName[p.defName])
.ToList();
}
/// <summary>
/// 获取所有直接子节点
/// </summary>
/// <param name="node">要查询的节点</param>
/// <returns>直接子节点列表,如果无子节点则返回空列表</returns>
public List<SkillTreeDef> GetAllDirectChildren(SkillTreeDef node)
{
if (node == null) return new List<SkillTreeDef>();
_childrenOfNode.TryGetValue(node.defName, out var children);
return children ?? new List<SkillTreeDef>();
}
/// <summary>
/// 获取所有祖先节点(包括直接和间接父节点),使用 BFS 算法避免循环依赖
/// </summary>
/// <param name="node">要查询的节点</param>
/// <returns>所有祖先节点列表</returns>
public List<SkillTreeDef> GetAllAncestors(SkillTreeDef node)
{
if (node == null) return new List<SkillTreeDef>();
var ancestors = new HashSet<SkillTreeDef>(); // 使用HashSet避免重复和循环
var queue = new Queue<SkillTreeDef>();
// 将当前节点的直接父节点作为起点加入队列
if (node.prerequisites != null)
{
foreach (var directParentRef in node.prerequisites)
{
if (directParentRef != null && _skillNodesByDefName.TryGetValue(directParentRef.defName, out var parentNode))
{
if (!ancestors.Contains(parentNode))
{
ancestors.Add(parentNode);
queue.Enqueue(parentNode);
}
}
}
}
while (queue.Count > 0)
{
var current = queue.Dequeue();
if (current.prerequisites != null)
{
foreach (var prerequisiteRef in current.prerequisites)
{
if (prerequisiteRef != null && _skillNodesByDefName.TryGetValue(prerequisiteRef.defName, out var actualParentNode))
{
if (!ancestors.Contains(actualParentNode))
{
ancestors.Add(actualParentNode);
queue.Enqueue(actualParentNode);
}
}
}
}
}
return ancestors.ToList();
}
/// <summary>
/// 获取所有后代节点(包括直接和间接子节点),使用 BFS 算法避免循环依赖
/// </summary>
/// <param name="node">要查询的节点</param>
/// <returns>所有后代节点列表</returns>
public List<SkillTreeDef> GetAllDescendants(SkillTreeDef node)
{
if (node == null) return new List<SkillTreeDef>();
var descendants = new HashSet<SkillTreeDef>(); // 使用HashSet避免重复和循环
var queue = new Queue<SkillTreeDef>();
// 将当前节点的直接子节点作为起点加入队列
_childrenOfNode.TryGetValue(node.defName, out var directChildren);
if (directChildren != null)
{
foreach (var child in directChildren)
{
if (!descendants.Contains(child))
{
descendants.Add(child);
queue.Enqueue(child);
}
}
}
while (queue.Count > 0)
{
var current = queue.Dequeue();
_childrenOfNode.TryGetValue(current.defName, out var children);
if (children != null)
{
foreach (var child in children)
{
if (!descendants.Contains(child))
{
descendants.Add(child);
queue.Enqueue(child);
}
}
}
}
return descendants.ToList();
}
}
}