mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 11:37:12 +08:00
(client) feat:健康给予,路径优化,结算界面,商店界面 (#60)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/60
This commit is contained in:
@@ -1,65 +1,87 @@
|
||||
using System;
|
||||
using Data;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks; // 引入 Task
|
||||
using Data;
|
||||
using Utils;
|
||||
|
||||
namespace Managers
|
||||
{
|
||||
public class SkillTreeManager : Utils.Singleton<SkillTreeManager>, ILaunchManager, ISavableSingleton
|
||||
public class SkillTreeManager : Singleton<SkillTreeManager>, ILaunchManager, ISavableSingleton
|
||||
{
|
||||
public string StepDescription => "正在加载技能树";
|
||||
// 用于缓存解锁技能树的 SkillTreeDef 列表,提高频繁访问的效率
|
||||
private List<SkillTreeDef> _cachedUnlockedSkillTreesList;
|
||||
private Dictionary<string, List<SkillTreeDef>> _childrenOfNode;
|
||||
|
||||
// 内部数据结构,用于高效查询和图遍历
|
||||
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 始终可用。
|
||||
SaveManager.Instance.RegisterSavable(this);
|
||||
}
|
||||
|
||||
|
||||
public void Init()
|
||||
// 解锁技能树的 HashSet,由 Savable 属性标记,用于存档
|
||||
[Savable] public HashSet<string> UnlockedSkillTrees { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有已解锁的技能树定义列表。此列表是缓存的,性能高效。
|
||||
/// </summary>
|
||||
public List<SkillTreeDef> UnlockedSkillTreesList =>
|
||||
_cachedUnlockedSkillTreesList ?? RebuildUnlockedSkillTreesCache();
|
||||
|
||||
/// <summary>
|
||||
/// 指示管理器是否已完成初始化。
|
||||
/// </summary>
|
||||
public bool Completed { get; set; } // 新增:实现 ILaunchManager 接口的 Completed 属性
|
||||
|
||||
public string StepDescription => "正在加载技能树";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化技能树管理器,加载并解析所有技能树定义。
|
||||
/// </summary>
|
||||
/// <returns>一个表示异步操作完成的 Task。</returns>
|
||||
public async Task Init() // 接口变更:方法签名变为 async Task
|
||||
{
|
||||
// 假设 DefineManager.Instance 存在且 QueryDefinesByType 方法可用
|
||||
// 如果 DefineManager 是单例,这里直接调用即可。
|
||||
// 否则,需要确保 DefineManager 在此之前已被正确初始化。
|
||||
var allSkillTreeNodes = DefineManager.Instance.QueryDefinesByType<SkillTreeDef>();
|
||||
// 增加检查,防止重复初始化
|
||||
if (Completed) return;
|
||||
|
||||
// 遵循不检查单例的原则,假定 DefineManager.Instance 始终可用。
|
||||
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>>();
|
||||
if (allSkillTreeNodes == null)
|
||||
{
|
||||
Completed = true; // 即使没有定义,也认为初始化流程已完成
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 填充基本查询字典
|
||||
foreach (var node in allSkillTreeNodes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(node.defName))
|
||||
{
|
||||
// 可以选择抛出异常或记录警告,取决于项目的错误处理策略
|
||||
// 例如: Log.Warning($"SkillTreeDef with empty defName found. Skipping.");
|
||||
// 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.");
|
||||
// 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>();
|
||||
}
|
||||
if (!_skillNodesByTag.ContainsKey(node.tag)) _skillNodesByTag[node.tag] = new List<SkillTreeDef>();
|
||||
|
||||
_skillNodesByTag[node.tag].Add(node);
|
||||
}
|
||||
@@ -69,9 +91,7 @@ namespace Managers
|
||||
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);
|
||||
}
|
||||
@@ -80,32 +100,34 @@ namespace Managers
|
||||
// 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}'.");
|
||||
// Log.Warning($"SkillTreeDef '{childNode.defName}' references unknown prerequisite '{prerequisite.defName}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 初始化解锁技能树的缓存列表
|
||||
RebuildUnlockedSkillTreesCache();
|
||||
|
||||
Completed = true; // 在所有初始化逻辑完成后设置 Completed 为 true
|
||||
// 由于 Init 方法内部当前没有真正的异步操作,不需要显式 await Task.CompletedTask;
|
||||
// 编译器会为同步的 async Task 方法自动生成一个已完成的 Task。
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清空技能树管理器加载的所有数据结构。
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
// 清理所有数据结构,释放内存
|
||||
@@ -114,17 +136,58 @@ namespace Managers
|
||||
_skillNodesByFaction?.Clear();
|
||||
_childrenOfNode?.Clear();
|
||||
|
||||
// 清理解锁技能树的缓存列表
|
||||
_cachedUnlockedSkillTreesList?.Clear();
|
||||
_cachedUnlockedSkillTreesList = null;
|
||||
|
||||
// 将引用设为null,有助于GC
|
||||
_skillNodesByDefName = null;
|
||||
_skillNodesByTag = null;
|
||||
_skillNodesByFaction = null;
|
||||
_childrenOfNode = null;
|
||||
|
||||
// 重要:UnlockedSkillTrees 是 Savable 属性,它的状态由 SaveManager 管理,
|
||||
// 通常不应在 Clear 方法中清空。Clear 仅清理运行时数据结构。
|
||||
|
||||
Completed = false; // 清理后将 Completed 置为 false
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当一个技能树被成功解锁时触发。
|
||||
/// </summary>
|
||||
public event Action<SkillTreeDef> OnSkillTreeUnlocked;
|
||||
|
||||
/// <summary>
|
||||
/// 当一个技能树被成功锁定时触发。
|
||||
/// </summary>
|
||||
public event Action<SkillTreeDef> OnSkillTreeLocked;
|
||||
|
||||
/// <summary>
|
||||
/// 根据当前 UnlockedSkillTrees 集合重建解锁技能树的缓存列表。
|
||||
/// </summary>
|
||||
private List<SkillTreeDef> RebuildUnlockedSkillTreesCache()
|
||||
{
|
||||
// 在尝试查询技能定义之前,确保 _skillNodesByDefName 字典已初始化。
|
||||
// 这可以防止在 Init() 尚未完全完成时访问缓存导致的空引用问题。
|
||||
if (_skillNodesByDefName == null)
|
||||
{
|
||||
// 返回空列表是更温和的处理方式,表示目前没有可用的技能定义。
|
||||
_cachedUnlockedSkillTreesList = new List<SkillTreeDef>();
|
||||
return _cachedUnlockedSkillTreesList;
|
||||
}
|
||||
|
||||
_cachedUnlockedSkillTreesList = new List<SkillTreeDef>(UnlockedSkillTrees.Count); // 预分配容量
|
||||
foreach (var skillTreeDefName in UnlockedSkillTrees)
|
||||
if (_skillNodesByDefName.TryGetValue(skillTreeDefName, out var skillDef) && skillDef != null)
|
||||
_cachedUnlockedSkillTreesList.Add(skillDef);
|
||||
|
||||
return _cachedUnlockedSkillTreesList;
|
||||
}
|
||||
|
||||
// --- 节点查询 ---
|
||||
|
||||
/// <summary>
|
||||
/// 根据定义名查询技能树节点
|
||||
/// 根据定义名查询技能树节点
|
||||
/// </summary>
|
||||
/// <param name="defName">技能树节点的定义名</param>
|
||||
/// <returns>匹配的 SkillTreeDef,如果未找到则为 null</returns>
|
||||
@@ -136,7 +199,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据标签查询技能树节点列表
|
||||
/// 根据标签查询技能树节点列表
|
||||
/// </summary>
|
||||
/// <param name="tag">技能树节点的标签</param>
|
||||
/// <returns>匹配的 SkillTreeDef 列表,如果未找到则返回空列表</returns>
|
||||
@@ -148,7 +211,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据派系名称查询技能树节点列表
|
||||
/// 根据派系名称查询技能树节点列表
|
||||
/// </summary>
|
||||
/// <param name="factionName">派系的定义名</param>
|
||||
/// <returns>匹配的 SkillTreeDef 列表,如果未找到则返回空列表</returns>
|
||||
@@ -167,7 +230,7 @@ namespace Managers
|
||||
// --- 父子关系查询 ---
|
||||
|
||||
/// <summary>
|
||||
/// 判断 targetNode 是否是 baseNode 的直接父节点
|
||||
/// 判断 targetNode 是否是 baseNode 的直接父节点
|
||||
/// </summary>
|
||||
/// <param name="baseNode">作为父节点候选的节点</param>
|
||||
/// <param name="targetNode">作为子节点候选的节点</param>
|
||||
@@ -180,7 +243,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断 targetNode 是否是 baseNode 的直接子节点
|
||||
/// 判断 targetNode 是否是 baseNode 的直接子节点
|
||||
/// </summary>
|
||||
/// <param name="baseNode">作为父节点的节点</param>
|
||||
/// <param name="targetNode">作为子节点候选的节点</param>
|
||||
@@ -194,7 +257,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断 ancestor 是否是 descendant 的任何一级父节点(包括直接和间接)
|
||||
/// 判断 ancestor 是否是 descendant 的任何一级父节点(包括直接和间接)
|
||||
/// </summary>
|
||||
/// <param name="ancestor">祖先节点候选</param>
|
||||
/// <param name="descendant">后代节点候选</param>
|
||||
@@ -207,7 +270,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 判断 descendant 是否是 ancestor 的任何一级子节点(包括直接和间接)
|
||||
/// 判断 descendant 是否是 ancestor 的任何一级子节点(包括直接和间接)
|
||||
/// </summary>
|
||||
/// <param name="descendant">后代节点候选</param>
|
||||
/// <param name="ancestor">祖先节点候选</param>
|
||||
@@ -222,13 +285,13 @@ namespace Managers
|
||||
// --- 获取所有相关节点 ---
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有直接父节点
|
||||
/// 获取所有直接父节点
|
||||
/// </summary>
|
||||
/// <param name="node">要查询的节点</param>
|
||||
/// <returns>直接父节点列表,如果无父节点则返回空列表</returns>
|
||||
public List<SkillTreeDef> GetAllDirectParents(SkillTreeDef node)
|
||||
{
|
||||
if (node == null || node.prerequisites == null) return new List<SkillTreeDef>();
|
||||
if (node?.prerequisites == null) return new List<SkillTreeDef>();
|
||||
// 确保返回的是真实存在于管理器中的节点,而不是可能无效的引用
|
||||
return node.prerequisites.Where(p => p != null && _skillNodesByDefName.ContainsKey(p.defName))
|
||||
.Select(p => _skillNodesByDefName[p.defName])
|
||||
@@ -236,7 +299,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有直接子节点
|
||||
/// 获取所有直接子节点
|
||||
/// </summary>
|
||||
/// <param name="node">要查询的节点</param>
|
||||
/// <returns>直接子节点列表,如果无子节点则返回空列表</returns>
|
||||
@@ -248,7 +311,7 @@ namespace Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有祖先节点(包括直接和间接父节点),使用 BFS 算法避免循环依赖
|
||||
/// 获取所有祖先节点(包括直接和间接父节点),使用 BFS 算法避免循环依赖
|
||||
/// </summary>
|
||||
/// <param name="node">要查询的节点</param>
|
||||
/// <returns>所有祖先节点列表</returns>
|
||||
@@ -261,47 +324,35 @@ namespace Managers
|
||||
|
||||
// 将当前节点的直接父节点作为起点加入队列
|
||||
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 算法避免循环依赖
|
||||
/// 获取所有后代节点(包括直接和间接子节点),使用 BFS 算法避免循环依赖
|
||||
/// </summary>
|
||||
/// <param name="node">要查询的节点</param>
|
||||
/// <returns>所有后代节点列表</returns>
|
||||
@@ -315,16 +366,12 @@ namespace Managers
|
||||
// 将当前节点的直接子节点作为起点加入队列
|
||||
_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)
|
||||
{
|
||||
@@ -332,65 +379,87 @@ namespace Managers
|
||||
|
||||
_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>
|
||||
/// 确保技能树定义名称有效。
|
||||
/// 获取指定名称的技能树定义,如果名称无效或未找到,则抛出 ArgumentException。
|
||||
/// </summary>
|
||||
/// <param name="skillTreeDefName">要验证的技能树定义名称。</param>
|
||||
/// <exception cref="ArgumentException">如果技能树定义名称无效(不在 _skillNodesByDefName 中),则抛出。</exception>
|
||||
private bool ValidateSkillTreeDefName(string skillTreeDefName)
|
||||
/// <param name="skillTreeDefName">要获取的技能树定义名称。</param>
|
||||
/// <returns>对应的 SkillTreeDef 对象。</returns>
|
||||
/// <exception cref="ArgumentException">如果 skillTreeDefName 为空或未找到对应的 SkillTreeDef。</exception>
|
||||
private SkillTreeDef GetValidSkillTreeDef(string skillTreeDefName)
|
||||
{
|
||||
return _skillNodesByDefName.ContainsKey(skillTreeDefName);
|
||||
// `TryGetValue` 成功但 `node` 仍为null理论上不发生,但为安全起见
|
||||
if (string.IsNullOrEmpty(skillTreeDefName)
|
||||
|| !_skillNodesByDefName.TryGetValue(skillTreeDefName, out var node)
|
||||
|| node == null)
|
||||
throw new ArgumentException($"Invalid or unknown SkillTreeDef name: '{skillTreeDefName}'.",
|
||||
nameof(skillTreeDefName));
|
||||
return node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试解锁一个技能树。
|
||||
/// 必须是有效的 skillTreeDefName,否则会抛出 ArgumentException。
|
||||
/// 尝试解锁一个技能树。
|
||||
/// 必须是有效的 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);
|
||||
var skillDef = GetValidSkillTreeDef(skillTreeDefName); // 使用新的验证方法,如果无效会抛出异常
|
||||
if (UnlockedSkillTrees.Add(skillTreeDefName))
|
||||
{
|
||||
RebuildUnlockedSkillTreesCache(); // 技能树状态改变,重建缓存
|
||||
OnSkillTreeUnlocked?.Invoke(skillDef); // 触发解锁事件
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试加锁(锁定)一个技能树。
|
||||
/// 必须是有效的 skillTreeDefName,否则会抛出 ArgumentException。
|
||||
/// 尝试加锁(锁定)一个技能树。
|
||||
/// 必须是有效的 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);
|
||||
var skillDef = GetValidSkillTreeDef(skillTreeDefName); // 使用新的验证方法,如果无效会抛出异常
|
||||
if (UnlockedSkillTrees.Remove(skillTreeDefName))
|
||||
{
|
||||
RebuildUnlockedSkillTreesCache(); // 技能树状态改变,重建缓存
|
||||
OnSkillTreeLocked?.Invoke(skillDef); // 触发锁定事件
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询指定技能树是否已解锁。
|
||||
/// 必须是有效的 skillTreeDefName,否则会抛出 ArgumentException。
|
||||
/// 查询指定技能树是否已解锁。
|
||||
/// 必须是有效的 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);
|
||||
// GetValidSkillTreeDef 会在 defName 无效时抛出 ArgumentException
|
||||
// 如果成功返回,说明 skillTreeDefName 是有效的
|
||||
GetValidSkillTreeDef(skillTreeDefName);
|
||||
return UnlockedSkillTrees.Contains(skillTreeDefName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user