From 0fd399e92eb4eb120bdc84a4787fc9665352758a Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Wed, 9 Oct 2024 00:42:58 -0400 Subject: [PATCH] Core/Players: Fixed trait config validation Port From (https://github.com/TrinityCore/TrinityCore/commit/1f0a62bde5b927392a58e95a87d4d8bd93438d7e) --- Source/Game/DataStorage/Structs/T_Records.cs | 1 + Source/Game/Entities/Player/Player.Spells.cs | 2 +- Source/Game/Entities/Player/Player.Talents.cs | 3 +- Source/Game/Spells/TraitManager.cs | 111 ++++++++++++------ 4 files changed, 78 insertions(+), 39 deletions(-) diff --git a/Source/Game/DataStorage/Structs/T_Records.cs b/Source/Game/DataStorage/Structs/T_Records.cs index 3b71e6f07..789746b0a 100644 --- a/Source/Game/DataStorage/Structs/T_Records.cs +++ b/Source/Game/DataStorage/Structs/T_Records.cs @@ -129,6 +129,7 @@ namespace Game.DataStorage public int TraitCondAccountElementID; public TraitConditionType GetCondType() { return (TraitConditionType)CondType; } + public bool HasFlag(TraitCondFlags flag) => Flags.HasAnyFlag((int)flag); } public sealed class TraitCostRecord diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index 0f009b2f8..e556186f7 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -2773,7 +2773,7 @@ namespace Game.Entities if (traitDefinitionId.HasValue) { TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitDefinitionId.Value); - if (traitDefinition != null) + if (traitDefinition != null && traitDefinition.OverridesSpellID != 0) AddOverrideSpell(traitDefinition.OverridesSpellID, spellId); } diff --git a/Source/Game/Entities/Player/Player.Talents.cs b/Source/Game/Entities/Player/Player.Talents.cs index f3efcbe61..8108e9486 100644 --- a/Source/Game/Entities/Player/Player.Talents.cs +++ b/Source/Game/Entities/Player/Player.Talents.cs @@ -1058,8 +1058,7 @@ namespace Game.Entities if (consumeCurrencies) { Dictionary currencies = new(); - foreach (TraitEntryPacket costEntry in costEntries) - TraitMgr.FillSpentCurrenciesMap(costEntry, currencies); + TraitMgr.FillSpentCurrenciesMap(costEntries, currencies); foreach (var (traitCurrencyId, amount) in currencies) { diff --git a/Source/Game/Spells/TraitManager.cs b/Source/Game/Spells/TraitManager.cs index cb04451be..d12b13c88 100644 --- a/Source/Game/Spells/TraitManager.cs +++ b/Source/Game/Spells/TraitManager.cs @@ -477,7 +477,7 @@ namespace Game } } - public static void FillSpentCurrenciesMap(TraitEntryPacket entry, Dictionary cachedCurrencies) + public static void AddSpentCurrenciesForEntry(TraitEntryPacket entry, Dictionary cachedCurrencies, int multiplier) { Node node = _traitNodes.LookupByKey(entry.TraitNodeID); foreach (NodeGroup group in node.Groups) @@ -487,7 +487,7 @@ namespace Game if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID)) cachedCurrencies[cost.TraitCurrencyID] = 0; - cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank; + cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank * multiplier; } } @@ -499,7 +499,7 @@ namespace Game if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID)) cachedCurrencies[cost.TraitCurrencyID] = 0; - cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank; + cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank * multiplier; } } @@ -508,7 +508,7 @@ namespace Game if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID)) cachedCurrencies[cost.TraitCurrencyID] = 0; - cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank; + cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank * multiplier; } Tree tree = _traitTrees.LookupByKey(node.Data.TraitTreeID); @@ -519,15 +519,15 @@ namespace Game if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID)) cachedCurrencies[cost.TraitCurrencyID] = 0; - cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank; + cachedCurrencies[cost.TraitCurrencyID] += cost.Amount * entry.Rank * multiplier; } } } - public static void FillSpentCurrenciesMap(TraitConfigPacket traitConfig, Dictionary cachedCurrencies) + public static void FillSpentCurrenciesMap(List traitEntries, Dictionary cachedCurrencies) { - foreach (TraitEntryPacket entry in traitConfig.Entries) - FillSpentCurrenciesMap(entry, cachedCurrencies); + foreach (TraitEntryPacket entry in traitEntries) + AddSpentCurrenciesForEntry(entry, cachedCurrencies, 1); } public static int[] GetClassAndSpecTreeCurrencies(TraitConfigPacket traitConfig) @@ -588,7 +588,7 @@ namespace Game if (cachedCurrencies == null) { cachedCurrencies = new(); - FillSpentCurrenciesMap(traitConfig, cachedCurrencies); + FillSpentCurrenciesMap(traitConfig.Entries, cachedCurrencies); } if (condition.TraitNodeGroupID != 0 || condition.TraitNodeID != 0 || condition.TraitNodeEntryID != 0) @@ -605,6 +605,68 @@ namespace Game return true; } + public static bool NodeMeetsTraitConditions(TraitConfigPacket traitConfig, Node node, uint traitNodeEntryId, Player player, Dictionary spentCurrencies) + { + var meetsConditions = (bool isSufficient, bool hasFailedConditions) (List conditions) => + { + bool isSufficient = false; + bool hasFailedConditions = false; + + foreach (var condition in conditions) + { + if (condition.GetCondType() == TraitConditionType.Available || condition.GetCondType() == TraitConditionType.Visible) + { + if (MeetsTraitCondition(traitConfig, player, condition, ref spentCurrencies)) + { + if (condition.HasFlag(TraitCondFlags.IsSufficient)) + { + isSufficient = true; + break; + } + continue; + } + + hasFailedConditions = true; + } + } + + return (isSufficient, hasFailedConditions); + }; + + bool IsSufficient; + bool HasFailedConditions; + + bool hasFailedConditions = false; + foreach (NodeEntry entry in node.Entries) + { + if (entry.Data.Id == traitNodeEntryId) + { + (IsSufficient, HasFailedConditions) = meetsConditions(entry.Conditions); + if (IsSufficient) + return true; + if (HasFailedConditions) + hasFailedConditions = true; + } + } + + (IsSufficient, HasFailedConditions) = meetsConditions(node.Conditions); + if (IsSufficient) + return true; + else if (HasFailedConditions) + hasFailedConditions = true; + + foreach (NodeGroup group in node.Groups) + { + (IsSufficient, HasFailedConditions) = meetsConditions(group.Conditions); + if (IsSufficient) + return true; + if (HasFailedConditions) + hasFailedConditions = true; + } + + return !hasFailedConditions; + } + public static List GetGrantedTraitEntriesForConfig(TraitConfigPacket traitConfig, Player player) { List entries = new(); @@ -701,24 +763,7 @@ namespace Game }; Dictionary spentCurrencies = new(); - FillSpentCurrenciesMap(traitConfig, spentCurrencies); - - bool meetsConditions(List conditions) - { - bool hasConditions = false; - foreach (TraitCondRecord condition in conditions) - { - if (condition.GetCondType() == TraitConditionType.Available || condition.GetCondType() == TraitConditionType.Visible) - { - if (MeetsTraitCondition(traitConfig, player, condition, ref spentCurrencies)) - return true; - - hasConditions = true; - } - } - - return !hasConditions; - } + FillSpentCurrenciesMap(traitConfig.Entries, spentCurrencies); var isValidTraitEntry = TalentLearnResult (TraitEntryPacket traitEntry) => { @@ -730,17 +775,9 @@ namespace Game if (getNodeEntryCount(traitEntry.TraitNodeID) != 1) return TalentLearnResult.FailedUnknown; - foreach (NodeEntry entry in node.Entries) - if (entry.Data.Id == traitEntry.TraitNodeEntryID && !meetsConditions(entry.Conditions)) - return TalentLearnResult.FailedUnknown; - - if (!meetsConditions(node.Conditions)) + if (!NodeMeetsTraitConditions(traitConfig, node, (uint)traitEntry.TraitNodeEntryID, player, spentCurrencies)) return TalentLearnResult.FailedUnknown; - foreach (NodeGroup group in node.Groups) - if (!meetsConditions(group.Conditions)) - return TalentLearnResult.FailedUnknown; - if (!node.ParentNodes.Empty()) { bool hasAnyParentTrait = false; @@ -772,6 +809,8 @@ namespace Game if (!removeInvalidEntries) return result; + AddSpentCurrenciesForEntry(traitConfig.Entries[i], spentCurrencies, -1); + if (traitConfig.Entries[i].GrantedRanks == 0 // fully remove entries that don't have granted ranks || traitConfig.Entries[i].Rank == 0) // ... or entries that do have them and don't have any additional spent ranks (can happen if the same entry is revalidated after first removing all spent ranks) traitConfig.Entries.RemoveAt(i);