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

401 lines
17 KiB
C#
Raw Normal View History

using System;
using Data;
using System.Collections.Generic;
using System.Linq;
namespace Managers
{
public class SkillTreeManager : Utils.Singleton<SkillTreeManager>, ILaunchManager, ISavableSingleton
{
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;
[Savable] public HashSet<string> UnlockedSkillTrees { get; set; } = new();
public SkillTreeManager()
{
SaveManager.Instance.RegisterSavable(this);
}
~SkillTreeManager()
{
SaveManager.Instance.UnregisterSavable(this);
}
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();
}
/// <summary>
/// 确保技能树定义名称有效。
/// </summary>
/// <param name="skillTreeDefName">要验证的技能树定义名称。</param>
/// <exception cref="ArgumentException">如果技能树定义名称无效(不在 _skillNodesByDefName 中),则抛出。</exception>
private bool ValidateSkillTreeDefName(string skillTreeDefName)
{
return _skillNodesByDefName.ContainsKey(skillTreeDefName);
}
/// <summary>
/// 尝试解锁一个技能树。
/// 必须是有效的 skillTreeDefName否则会抛出 ArgumentException。
/// </summary>
/// <param name="skillTreeDefName">要解锁的技能树的定义名称。</param>
/// <returns>如果技能树成功解锁(之前未解锁),则为 true如果技能树已经解锁则为 false。</returns>
/// <exception cref="ArgumentException">如果 skillTreeDefName 无效。</exception>
public bool UnlockSkillTree(string skillTreeDefName)
{
return ValidateSkillTreeDefName(skillTreeDefName) && UnlockedSkillTrees.Add(skillTreeDefName);
}
/// <summary>
/// 尝试加锁(锁定)一个技能树。
/// 必须是有效的 skillTreeDefName否则会抛出 ArgumentException。
/// </summary>
/// <param name="skillTreeDefName">要加锁的技能树的定义名称。</param>
/// <returns>如果技能树成功加锁(之前已解锁),则为 true如果技能树已经加锁则为 false。</returns>
/// <exception cref="ArgumentException">如果 skillTreeDefName 无效。</exception>
public bool LockSkillTree(string skillTreeDefName)
{
return ValidateSkillTreeDefName(skillTreeDefName) && UnlockedSkillTrees.Remove(skillTreeDefName);
}
/// <summary>
/// 查询指定技能树是否已解锁。
/// 必须是有效的 skillTreeDefName否则会抛出 ArgumentException。
/// </summary>
/// <param name="skillTreeDefName">要查询的技能树的定义名称。</param>
/// <returns>如果技能树已解锁,则为 true否则为 false。</returns>
/// <exception cref="ArgumentException">如果 skillTreeDefName 无效。</exception>
public bool IsSkillTreeUnlocked(string skillTreeDefName)
{
return ValidateSkillTreeDefName(skillTreeDefName) && UnlockedSkillTrees.Contains(skillTreeDefName);
}
}
}