Core/Players: Implemented hero talents
Port From (https://github.com/TrinityCore/TrinityCore/commit/d1ffe61727f53505a3e2b94cae32f2ce3d28b07b)
This commit is contained in:
@@ -1497,6 +1497,12 @@ namespace Framework.Database
|
||||
PrepareStatement(HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_NODE_ENTRY, "SELECT ID, TraitNodeID, TraitNodeEntryID, `Index` FROM trait_node_x_trait_node_entry" +
|
||||
" WHERE (`VerifiedBuild` > 0) = ?");
|
||||
|
||||
// TraitSubTree.db2
|
||||
PrepareStatement(HotfixStatements.SEL_TRAIT_SUB_TREE, "SELECT Name, Description, ID, UiTextureAtlasElementID, TraitTreeID FROM trait_sub_tree" +
|
||||
" WHERE (`VerifiedBuild` > 0) = ?");
|
||||
PrepareStatement(HotfixStatements.SEL_TRAIT_SUB_TREE_LOCALE, "SELECT ID, Name_lang, Description_lang FROM trait_sub_tree_locale WHERE (`VerifiedBuild` > 0) = ?" +
|
||||
" AND locale = ?");
|
||||
|
||||
// TraitTree.db2
|
||||
PrepareStatement(HotfixStatements.SEL_TRAIT_TREE, "SELECT ID, TraitSystemID, Unused1000_1, FirstTraitNodeID, PlayerConditionID, Flags, Unused1000_2, " +
|
||||
"Unused1000_3 FROM trait_tree WHERE (`VerifiedBuild` > 0) = ?");
|
||||
@@ -2350,6 +2356,9 @@ namespace Framework.Database
|
||||
|
||||
SEL_TRAIT_NODE_X_TRAIT_NODE_ENTRY,
|
||||
|
||||
SEL_TRAIT_SUB_TREE,
|
||||
SEL_TRAIT_SUB_TREE_LOCALE,
|
||||
|
||||
SEL_TRAIT_TREE,
|
||||
|
||||
SEL_TRAIT_TREE_LOADOUT,
|
||||
|
||||
@@ -365,6 +365,7 @@ namespace Game.DataStorage
|
||||
TraitNodeXTraitCondStorage = ReadDB2<TraitNodeXTraitCondRecord>("TraitNodeXTraitCond.db2", HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_COND);
|
||||
TraitNodeXTraitCostStorage = ReadDB2<TraitNodeXTraitCostRecord>("TraitNodeXTraitCost.db2", HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_COST);
|
||||
TraitNodeXTraitNodeEntryStorage = ReadDB2<TraitNodeXTraitNodeEntryRecord>("TraitNodeXTraitNodeEntry.db2", HotfixStatements.SEL_TRAIT_NODE_X_TRAIT_NODE_ENTRY);
|
||||
TraitSubTreeStorage = ReadDB2<TraitSubTreeRecord>("TraitSubTree.db2", HotfixStatements.SEL_TRAIT_SUB_TREE, HotfixStatements.SEL_TRAIT_SUB_TREE_LOCALE);
|
||||
TraitTreeStorage = ReadDB2<TraitTreeRecord>("TraitTree.db2", HotfixStatements.SEL_TRAIT_TREE);
|
||||
TraitTreeLoadoutStorage = ReadDB2<TraitTreeLoadoutRecord>("TraitTreeLoadout.db2", HotfixStatements.SEL_TRAIT_TREE_LOADOUT);
|
||||
TraitTreeLoadoutEntryStorage = ReadDB2<TraitTreeLoadoutEntryRecord>("TraitTreeLoadoutEntry.db2", HotfixStatements.SEL_TRAIT_TREE_LOADOUT_ENTRY);
|
||||
@@ -811,6 +812,7 @@ namespace Game.DataStorage
|
||||
public static DB6Storage<TraitNodeXTraitCondRecord> TraitNodeXTraitCondStorage;
|
||||
public static DB6Storage<TraitNodeXTraitCostRecord> TraitNodeXTraitCostStorage;
|
||||
public static DB6Storage<TraitNodeXTraitNodeEntryRecord> TraitNodeXTraitNodeEntryStorage;
|
||||
public static DB6Storage<TraitSubTreeRecord> TraitSubTreeStorage;
|
||||
public static DB6Storage<TraitTreeRecord> TraitTreeStorage;
|
||||
public static DB6Storage<TraitTreeLoadoutRecord> TraitTreeLoadoutStorage;
|
||||
public static DB6Storage<TraitTreeLoadoutEntryRecord> TraitTreeLoadoutEntryStorage;
|
||||
|
||||
@@ -283,6 +283,15 @@ namespace Game.DataStorage
|
||||
public int Index;
|
||||
}
|
||||
|
||||
public sealed class TraitSubTreeRecord
|
||||
{
|
||||
public LocalizedString Name;
|
||||
public LocalizedString Description;
|
||||
public uint ID;
|
||||
public int UiTextureAtlasElementID;
|
||||
public int TraitTreeID; // Parent tree
|
||||
}
|
||||
|
||||
public sealed class TraitTreeRecord
|
||||
{
|
||||
public uint Id;
|
||||
|
||||
@@ -1124,8 +1124,12 @@ namespace Game.Entities
|
||||
if (TraitMgr.ValidateConfig(traitConfig, this, false, true) != TalentLearnResult.LearnOk)
|
||||
{
|
||||
traitConfig.Entries.Clear();
|
||||
traitConfig.SubTrees.Clear();
|
||||
foreach (TraitEntry grantedEntry in TraitMgr.GetGrantedTraitEntriesForConfig(traitConfig, this))
|
||||
traitConfig.Entries.Add(new TraitEntryPacket(grantedEntry));
|
||||
|
||||
// rebuild subtrees
|
||||
TraitMgr.ValidateConfig(traitConfig, this, false, true);
|
||||
}
|
||||
|
||||
AddTraitConfig(traitConfig);
|
||||
|
||||
@@ -899,6 +899,25 @@ namespace Game.Entities
|
||||
newEntry.GrantedRanks = traitEntry.GrantedRanks;
|
||||
AddDynamicUpdateFieldValue(setter.ModifyValue(setter.Entries), newEntry);
|
||||
}
|
||||
|
||||
foreach (var traitSubTree in traitConfig.SubTrees)
|
||||
{
|
||||
TraitSubTreeCache newSubTree = new();
|
||||
newSubTree.TraitSubTreeID = traitSubTree.TraitSubTreeID;
|
||||
newSubTree.Active = traitSubTree.Active ? 1 : 0u;
|
||||
|
||||
foreach (var traitEntry in traitSubTree.Entries)
|
||||
{
|
||||
TraitEntry newEntry = new();
|
||||
newEntry.TraitNodeID = traitEntry.TraitNodeID;
|
||||
newEntry.TraitNodeEntryID = traitEntry.TraitNodeEntryID;
|
||||
newEntry.Rank = traitEntry.Rank;
|
||||
newEntry.GrantedRanks = traitEntry.GrantedRanks;
|
||||
newSubTree.Entries.Add(newEntry);
|
||||
}
|
||||
|
||||
AddDynamicUpdateFieldValue(setter.ModifyValue(setter.SubTrees), newSubTree);
|
||||
}
|
||||
}
|
||||
|
||||
public TraitConfig GetTraitConfig(int configId)
|
||||
@@ -1062,7 +1081,49 @@ namespace Game.Entities
|
||||
}
|
||||
}
|
||||
|
||||
m_traitConfigStates[(int)editedConfigId] = PlayerSpellState.Changed;
|
||||
for (int i = 0; i < newConfig.SubTrees.Count; ++i)
|
||||
{
|
||||
var newSubTree = newConfig.SubTrees[i];
|
||||
int oldSubTreeIndex = editedConfig.SubTrees.FindIndexIf(ufSubTree => ufSubTree.TraitSubTreeID == newSubTree.TraitSubTreeID);
|
||||
|
||||
List<TraitEntry> subTreeEntries = new();
|
||||
for (int j = 0; j < newSubTree.Entries.Count; ++j)
|
||||
{
|
||||
TraitEntry newUfEntry = subTreeEntries[j];
|
||||
newUfEntry.TraitNodeID = newSubTree.Entries[j].TraitNodeID;
|
||||
newUfEntry.TraitNodeEntryID = newSubTree.Entries[j].TraitNodeEntryID;
|
||||
newUfEntry.Rank = newSubTree.Entries[j].Rank;
|
||||
newUfEntry.GrantedRanks = newSubTree.Entries[j].GrantedRanks;
|
||||
}
|
||||
if (oldSubTreeIndex < 0)
|
||||
{
|
||||
TraitSubTreeCache newUfSubTree = new();
|
||||
newUfSubTree.TraitSubTreeID = newSubTree.TraitSubTreeID;
|
||||
newUfSubTree.Active = newSubTree.Active ? 1 : 0u;
|
||||
newUfSubTree.Entries = subTreeEntries;
|
||||
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, editedIndex);
|
||||
AddDynamicUpdateFieldValue(traitConfig.ModifyValue(traitConfig.SubTrees), newUfSubTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool wasActive = m_activePlayerData.TraitConfigs[editedIndex].SubTrees[oldSubTreeIndex].Active != 0;
|
||||
|
||||
TraitConfig traitConfig = m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TraitConfigs, editedIndex);
|
||||
TraitSubTreeCache traitSubTreeCache = traitConfig.ModifyValue(traitConfig.SubTrees, oldSubTreeIndex);
|
||||
|
||||
traitSubTreeCache.Active = newSubTree.Active ? 1 : 0u;
|
||||
traitSubTreeCache.Entries = subTreeEntries;
|
||||
SetUpdateFieldValue(traitConfig.ModifyValue(traitConfig.SubTrees, oldSubTreeIndex), traitSubTreeCache);
|
||||
|
||||
|
||||
if (applyTraits && wasActive != newSubTree.Active)
|
||||
foreach (var subTreeEntry in newSubTree.Entries)
|
||||
ApplyTraitEntry(subTreeEntry.TraitNodeEntryID, subTreeEntry.Rank, subTreeEntry.GrantedRanks, newSubTree.Active);
|
||||
}
|
||||
}
|
||||
|
||||
m_traitConfigStates[editedConfigId] = PlayerSpellState.Changed;
|
||||
}
|
||||
|
||||
public void RenameTraitConfig(int editedConfigId, string newName)
|
||||
@@ -1106,6 +1167,7 @@ namespace Game.Entities
|
||||
return;
|
||||
|
||||
foreach (TraitEntry traitEntry in traitConfig.Entries)
|
||||
if (!apply || TraitMgr.CanApplyTraitNode(traitConfig, traitEntry))
|
||||
ApplyTraitEntry(traitEntry.TraitNodeEntryID, traitEntry.Rank, traitEntry.GrantedRanks, apply);
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,8 @@ namespace Game
|
||||
return;
|
||||
}
|
||||
|
||||
if (traitNodeEntry.TraitDefinitionID != 0)
|
||||
{
|
||||
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitNodeEntry.TraitDefinitionID);
|
||||
if (traitDefinition == null)
|
||||
{
|
||||
@@ -89,6 +91,7 @@ namespace Game
|
||||
SendPacket(new TraitConfigCommitFailed(configId, traitDefinition.VisibleSpellID, (int)TalentLearnResult.FailedCantRemoveTalent));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
hasRemovedEntries = true;
|
||||
}
|
||||
|
||||
@@ -148,19 +148,19 @@ namespace Game.Networking.Packets
|
||||
}
|
||||
}
|
||||
|
||||
public class TraitSubTreeCache
|
||||
public class TraitSubTreeCachePacket
|
||||
{
|
||||
public int TraitSubTreeID;
|
||||
public List<TraitEntryPacket> Entries = new();
|
||||
public bool Active;
|
||||
|
||||
public TraitSubTreeCache() { }
|
||||
public TraitSubTreeCache(TraitSubTreeCache ufSubTreeCache)
|
||||
public TraitSubTreeCachePacket() { }
|
||||
public TraitSubTreeCachePacket(TraitSubTreeCache ufSubTreeCache)
|
||||
{
|
||||
TraitSubTreeID = ufSubTreeCache.TraitSubTreeID;
|
||||
foreach (var ufEntry in ufSubTreeCache.Entries)
|
||||
Entries.Add(ufEntry);
|
||||
Active = ufSubTreeCache.Active;
|
||||
Entries.Add(new TraitEntryPacket(ufEntry));
|
||||
Active = ufSubTreeCache.Active == 0 ? false : true;
|
||||
}
|
||||
|
||||
public void Read(WorldPacket data)
|
||||
@@ -203,7 +203,7 @@ namespace Game.Networking.Packets
|
||||
public uint SkillLineID;
|
||||
public int TraitSystemID;
|
||||
public List<TraitEntryPacket> Entries = new();
|
||||
public List<TraitSubTreeCache> SubTrees = new();
|
||||
public List<TraitSubTreeCachePacket> SubTrees = new();
|
||||
public string Name = "";
|
||||
|
||||
public TraitConfigPacket() { }
|
||||
@@ -219,6 +219,8 @@ namespace Game.Networking.Packets
|
||||
TraitSystemID = ufConfig.TraitSystemID;
|
||||
foreach (TraitEntry ufEntry in ufConfig.Entries)
|
||||
Entries.Add(new TraitEntryPacket(ufEntry));
|
||||
foreach (var ufSubTree in ufConfig.SubTrees)
|
||||
SubTrees.Add(new TraitSubTreeCachePacket(ufSubTree));
|
||||
Name = ufConfig.Name;
|
||||
}
|
||||
|
||||
@@ -257,7 +259,7 @@ namespace Game.Networking.Packets
|
||||
|
||||
for (var i = 0; i < subtreesSize; ++i)
|
||||
{
|
||||
TraitSubTreeCache subtrees = new();
|
||||
TraitSubTreeCachePacket subtrees = new();
|
||||
subtrees.Read(data);
|
||||
SubTrees.Add(subtrees);
|
||||
}
|
||||
@@ -294,7 +296,7 @@ namespace Game.Networking.Packets
|
||||
|
||||
data.WriteBits(Name.GetByteCount(), 9);
|
||||
|
||||
foreach (TraitSubTreeCache traitSubTreeCache in SubTrees)
|
||||
foreach (TraitSubTreeCachePacket traitSubTreeCache in SubTrees)
|
||||
traitSubTreeCache.Write(data);
|
||||
|
||||
data.FlushBits();
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Game
|
||||
|
||||
static Dictionary<int, NodeGroup> _traitGroups = new();
|
||||
static Dictionary<int, Node> _traitNodes = new();
|
||||
static Dictionary<int, SubTree> _traitSubTrees = new();
|
||||
static Dictionary<int, Tree> _traitTrees = new();
|
||||
static int[] _skillLinesByClass = new int[(int)Class.Max];
|
||||
static MultiMap<int, Tree> _traitTreesBySkillLine = new();
|
||||
@@ -96,12 +97,11 @@ namespace Game
|
||||
treeCosts.Add(traitTreeXTraitCostEntry.TraitTreeID, traitCostEntry);
|
||||
}
|
||||
|
||||
MultiMap<int, TraitCurrencyRecord> treeCurrencies = new();
|
||||
MultiMap<int, TraitTreeXTraitCurrencyRecord> treeCurrencies = new();
|
||||
foreach (TraitTreeXTraitCurrencyRecord traitTreeXTraitCurrencyEntry in CliDB.TraitTreeXTraitCurrencyStorage.Values)
|
||||
{
|
||||
TraitCurrencyRecord traitCurrencyEntry = CliDB.TraitCurrencyStorage.LookupByKey(traitTreeXTraitCurrencyEntry.TraitCurrencyID);
|
||||
if (traitCurrencyEntry != null)
|
||||
treeCurrencies.Add(traitTreeXTraitCurrencyEntry.TraitTreeID, traitCurrencyEntry);
|
||||
if (CliDB.TraitCurrencyStorage.HasRecord((uint)traitTreeXTraitCurrencyEntry.TraitCurrencyID))
|
||||
treeCurrencies.Add(traitTreeXTraitCurrencyEntry.TraitTreeID, traitTreeXTraitCurrencyEntry);
|
||||
}
|
||||
|
||||
MultiMap<int, int> traitTreesIdsByTraitSystem = new();
|
||||
@@ -116,7 +116,10 @@ namespace Game
|
||||
|
||||
var currencies = treeCurrencies.LookupByKey(traitTree.Id);
|
||||
if (currencies != null)
|
||||
tree.Currencies = currencies;
|
||||
{
|
||||
currencies.OrderBy(p => p.Index);
|
||||
tree.Currencies.AddRange(currencies.Select(p => CliDB.TraitCurrencyStorage.LookupByKey(p.TraitCurrencyID)));
|
||||
}
|
||||
|
||||
if (traitTree.TraitSystemID != 0)
|
||||
{
|
||||
@@ -127,7 +130,19 @@ namespace Game
|
||||
_traitTrees[(int)traitTree.Id] = tree;
|
||||
}
|
||||
|
||||
foreach (TraitNodeGroupRecord traitNodeGroup in CliDB.TraitNodeGroupStorage.Values)
|
||||
foreach (var (_, traitSubTree) in CliDB.TraitSubTreeStorage)
|
||||
{
|
||||
SubTree subTree = new();
|
||||
subTree.Data = traitSubTree;
|
||||
|
||||
Tree tree = _traitTrees.LookupByKey(traitSubTree.TraitTreeID);
|
||||
if (tree != null)
|
||||
tree.SubTrees.Add(subTree);
|
||||
|
||||
_traitSubTrees[(int)traitSubTree.ID] = subTree;
|
||||
}
|
||||
|
||||
foreach (var (_, traitNodeGroup) in CliDB.TraitNodeGroupStorage)
|
||||
{
|
||||
NodeGroup nodeGroup = new();
|
||||
nodeGroup.Data = traitNodeGroup;
|
||||
@@ -186,6 +201,50 @@ namespace Game
|
||||
if (costs1 != null)
|
||||
node.Costs = costs1;
|
||||
|
||||
SubTree subTree = _traitSubTrees.LookupByKey(traitNode.TraitSubTreeID);
|
||||
if (subTree != null)
|
||||
{
|
||||
subTree.Nodes.Add(node);
|
||||
|
||||
foreach (NodeEntry nodeEntry in node.Entries)
|
||||
{
|
||||
foreach (TraitCostRecord cost in nodeEntry.Costs)
|
||||
{
|
||||
TraitCurrencyRecord traitCurrency = CliDB.TraitCurrencyStorage.LookupByKey(cost.TraitCurrencyID);
|
||||
if (traitCurrency != null)
|
||||
subTree.Currencies.Add(traitCurrency);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (NodeGroup nodeGroup in node.Groups)
|
||||
{
|
||||
foreach (TraitCostRecord cost in nodeGroup.Costs)
|
||||
{
|
||||
TraitCurrencyRecord traitCurrency = CliDB.TraitCurrencyStorage.LookupByKey(cost.TraitCurrencyID);
|
||||
if (traitCurrency != null)
|
||||
subTree.Currencies.Add(traitCurrency);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TraitCostRecord cost in node.Costs)
|
||||
{
|
||||
TraitCurrencyRecord traitCurrency = CliDB.TraitCurrencyStorage.LookupByKey(cost.TraitCurrencyID);
|
||||
if (traitCurrency != null)
|
||||
subTree.Currencies.Add(traitCurrency);
|
||||
}
|
||||
|
||||
Tree tree1 = _traitTrees.LookupByKey(traitNode.TraitTreeID);
|
||||
if (tree1 != null)
|
||||
{
|
||||
foreach (TraitCostRecord cost in tree1.Costs)
|
||||
{
|
||||
TraitCurrencyRecord traitCurrency = CliDB.TraitCurrencyStorage.LookupByKey(cost.TraitCurrencyID);
|
||||
if (traitCurrency != null)
|
||||
subTree.Currencies.Add(traitCurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_traitNodes[(int)traitNode.Id] = node;
|
||||
}
|
||||
|
||||
@@ -471,6 +530,41 @@ namespace Game
|
||||
FillSpentCurrenciesMap(entry, cachedCurrencies);
|
||||
}
|
||||
|
||||
public static int[] GetClassAndSpecTreeCurrencies(TraitConfigPacket traitConfig)
|
||||
{
|
||||
int[] currencies = new int[2];
|
||||
|
||||
List<Tree> trees = GetTreesForConfig(traitConfig);
|
||||
if (trees != null)
|
||||
{
|
||||
int destIndex = 0;
|
||||
foreach (var tree in trees)
|
||||
{
|
||||
if (destIndex >= currencies.Length)
|
||||
break;
|
||||
|
||||
foreach (var currency in tree.Currencies)
|
||||
{
|
||||
if (destIndex >= currencies.Length)
|
||||
break;
|
||||
|
||||
currencies[destIndex++] = (int)currency.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return currencies;
|
||||
}
|
||||
|
||||
public static List<TraitCurrencyRecord> GetSubTreeCurrency(int traitSubTreeId)
|
||||
{
|
||||
SubTree subTree = _traitSubTrees.LookupByKey(traitSubTreeId);
|
||||
if (subTree == null)
|
||||
return null;
|
||||
|
||||
return subTree.Currencies;
|
||||
}
|
||||
|
||||
public static bool MeetsTraitCondition(TraitConfigPacket traitConfig, Player player, TraitCondRecord condition, ref Dictionary<int, int> cachedCurrencies)
|
||||
{
|
||||
if (condition.QuestID != 0 && !player.IsQuestRewarded(condition.QuestID))
|
||||
@@ -643,7 +737,7 @@ namespace Game
|
||||
return TalentLearnResult.FailedUnknown;
|
||||
|
||||
foreach (NodeEntry entry in node.Entries)
|
||||
if (!meetsConditions(entry.Conditions))
|
||||
if (entry.Data.Id == traitEntry.TraitNodeEntryID && !meetsConditions(entry.Conditions))
|
||||
return TalentLearnResult.FailedUnknown;
|
||||
|
||||
if (!meetsConditions(node.Conditions))
|
||||
@@ -697,6 +791,41 @@ namespace Game
|
||||
++i;
|
||||
}
|
||||
|
||||
Dictionary<int, SubtreeValidationData> subtrees = new();
|
||||
|
||||
foreach (TraitEntryPacket traitEntry in traitConfig.Entries)
|
||||
{
|
||||
Node node = _traitNodes.LookupByKey(traitEntry.TraitNodeID);
|
||||
var entryItr = node.Entries.Find(nodeEntry => nodeEntry.Data.Id == traitEntry.TraitNodeEntryID);
|
||||
Cypher.Assert(entryItr != null);
|
||||
|
||||
if (!subtrees.ContainsKey(entryItr.Data.TraitSubTreeID))
|
||||
subtrees[entryItr.Data.TraitSubTreeID] = new();
|
||||
|
||||
if (node.Data.GetNodeType() == TraitNodeType.SubTreeSelection)
|
||||
subtrees[entryItr.Data.TraitSubTreeID].IsSelected = true;
|
||||
|
||||
if (node.Data.TraitSubTreeID != 0)
|
||||
subtrees[node.Data.TraitSubTreeID].Entries.Add(traitEntry);
|
||||
}
|
||||
|
||||
foreach (TraitSubTreeCachePacket subTree in traitConfig.SubTrees)
|
||||
subTree.Active = false;
|
||||
|
||||
foreach (var (selectedSubTreeId, data) in subtrees)
|
||||
{
|
||||
var subtreeDataItr = traitConfig.SubTrees.Find(p => p.TraitSubTreeID == selectedSubTreeId);
|
||||
if (subtreeDataItr == null)
|
||||
{
|
||||
subtreeDataItr = new TraitSubTreeCachePacket();
|
||||
subtreeDataItr.TraitSubTreeID = selectedSubTreeId;
|
||||
traitConfig.SubTrees.Add(subtreeDataItr);
|
||||
}
|
||||
|
||||
subtreeDataItr.Entries = data.Entries;
|
||||
subtreeDataItr.Active = data.IsSelected;
|
||||
}
|
||||
|
||||
Dictionary<int, int> grantedCurrencies = new();
|
||||
FillOwnedCurrenciesMap(traitConfig, player, grantedCurrencies);
|
||||
|
||||
@@ -716,8 +845,10 @@ namespace Game
|
||||
|
||||
if (requireSpendingAllCurrencies && traitConfig.Type == TraitConfigType.Combat)
|
||||
{
|
||||
foreach (var (traitCurrencyId, grantedAmount) in grantedCurrencies)
|
||||
// client checks only first two currencies for trait tree
|
||||
foreach (int traitCurrencyId in GetClassAndSpecTreeCurrencies(traitConfig))
|
||||
{
|
||||
int grantedAmount = grantedCurrencies.LookupByKey(traitCurrencyId);
|
||||
if (grantedAmount == 0)
|
||||
continue;
|
||||
|
||||
@@ -725,11 +856,44 @@ namespace Game
|
||||
if (spentAmount == 0 || spentAmount != grantedAmount)
|
||||
return TalentLearnResult.UnspentTalentPoints;
|
||||
}
|
||||
|
||||
foreach (var (selectedTraitSubTreeId, data) in subtrees)
|
||||
{
|
||||
if (!data.IsSelected)
|
||||
continue;
|
||||
|
||||
foreach (TraitCurrencyRecord subTreeCurrency in GetSubTreeCurrency(selectedTraitSubTreeId))
|
||||
{
|
||||
int grantedAmount = grantedCurrencies.LookupByKey(subTreeCurrency.Id);
|
||||
if (grantedAmount == 0)
|
||||
continue;
|
||||
|
||||
int spentAmount = spentCurrencies.LookupByKey(subTreeCurrency.Id);
|
||||
if (spentAmount == 0 || spentAmount != grantedAmount)
|
||||
return TalentLearnResult.UnspentTalentPoints;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TalentLearnResult.LearnOk;
|
||||
}
|
||||
|
||||
public static bool CanApplyTraitNode(TraitConfig traitConfig, TraitEntry traitEntry)
|
||||
{
|
||||
Node node = _traitNodes.LookupByKey(traitEntry.TraitNodeID);
|
||||
if (node == null)
|
||||
return false;
|
||||
|
||||
if (node.Data.TraitSubTreeID != 0)
|
||||
{
|
||||
var subTreeItr = traitConfig.SubTrees._values.Find(p => p.TraitSubTreeID == node.Data.TraitSubTreeID);
|
||||
if (subTreeItr == null || subTreeItr.Active == 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<TraitDefinitionEffectPointsRecord> GetTraitDefinitionEffectPointModifiers(int traitDefinitionId)
|
||||
{
|
||||
return _traitDefinitionEffectPointModifiers.LookupByKey(traitDefinitionId);
|
||||
@@ -819,12 +983,26 @@ namespace Game
|
||||
public List<Node> Nodes = new();
|
||||
}
|
||||
|
||||
class SubTree
|
||||
{
|
||||
public TraitSubTreeRecord Data;
|
||||
public List<Node> Nodes = new();
|
||||
public List<TraitCurrencyRecord> Currencies = new();
|
||||
}
|
||||
|
||||
class Tree
|
||||
{
|
||||
public TraitTreeRecord Data;
|
||||
public List<Node> Nodes = new();
|
||||
public List<TraitCostRecord> Costs = new();
|
||||
public List<TraitCurrencyRecord> Currencies = new();
|
||||
public List<SubTree> SubTrees = new();
|
||||
public TraitConfigType ConfigType;
|
||||
}
|
||||
|
||||
class SubtreeValidationData
|
||||
{
|
||||
public List<TraitEntryPacket> Entries = new();
|
||||
public bool IsSelected;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user