Core/Players: Fixed trait config validation
Port From (https://github.com/TrinityCore/TrinityCore/commit/1f0a62bde5b927392a58e95a87d4d8bd93438d7e)
This commit is contained in:
@@ -129,6 +129,7 @@ namespace Game.DataStorage
|
|||||||
public int TraitCondAccountElementID;
|
public int TraitCondAccountElementID;
|
||||||
|
|
||||||
public TraitConditionType GetCondType() { return (TraitConditionType)CondType; }
|
public TraitConditionType GetCondType() { return (TraitConditionType)CondType; }
|
||||||
|
public bool HasFlag(TraitCondFlags flag) => Flags.HasAnyFlag((int)flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class TraitCostRecord
|
public sealed class TraitCostRecord
|
||||||
|
|||||||
@@ -2773,7 +2773,7 @@ namespace Game.Entities
|
|||||||
if (traitDefinitionId.HasValue)
|
if (traitDefinitionId.HasValue)
|
||||||
{
|
{
|
||||||
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitDefinitionId.Value);
|
TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitDefinitionId.Value);
|
||||||
if (traitDefinition != null)
|
if (traitDefinition != null && traitDefinition.OverridesSpellID != 0)
|
||||||
AddOverrideSpell(traitDefinition.OverridesSpellID, spellId);
|
AddOverrideSpell(traitDefinition.OverridesSpellID, spellId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1058,8 +1058,7 @@ namespace Game.Entities
|
|||||||
if (consumeCurrencies)
|
if (consumeCurrencies)
|
||||||
{
|
{
|
||||||
Dictionary<int, int> currencies = new();
|
Dictionary<int, int> currencies = new();
|
||||||
foreach (TraitEntryPacket costEntry in costEntries)
|
TraitMgr.FillSpentCurrenciesMap(costEntries, currencies);
|
||||||
TraitMgr.FillSpentCurrenciesMap(costEntry, currencies);
|
|
||||||
|
|
||||||
foreach (var (traitCurrencyId, amount) in currencies)
|
foreach (var (traitCurrencyId, amount) in currencies)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -477,7 +477,7 @@ namespace Game
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void FillSpentCurrenciesMap(TraitEntryPacket entry, Dictionary<int, int> cachedCurrencies)
|
public static void AddSpentCurrenciesForEntry(TraitEntryPacket entry, Dictionary<int, int> cachedCurrencies, int multiplier)
|
||||||
{
|
{
|
||||||
Node node = _traitNodes.LookupByKey(entry.TraitNodeID);
|
Node node = _traitNodes.LookupByKey(entry.TraitNodeID);
|
||||||
foreach (NodeGroup group in node.Groups)
|
foreach (NodeGroup group in node.Groups)
|
||||||
@@ -487,7 +487,7 @@ namespace Game
|
|||||||
if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID))
|
if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID))
|
||||||
cachedCurrencies[cost.TraitCurrencyID] = 0;
|
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))
|
if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID))
|
||||||
cachedCurrencies[cost.TraitCurrencyID] = 0;
|
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))
|
if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID))
|
||||||
cachedCurrencies[cost.TraitCurrencyID] = 0;
|
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);
|
Tree tree = _traitTrees.LookupByKey(node.Data.TraitTreeID);
|
||||||
@@ -519,15 +519,15 @@ namespace Game
|
|||||||
if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID))
|
if (!cachedCurrencies.ContainsKey(cost.TraitCurrencyID))
|
||||||
cachedCurrencies[cost.TraitCurrencyID] = 0;
|
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<int, int> cachedCurrencies)
|
public static void FillSpentCurrenciesMap(List<TraitEntryPacket> traitEntries, Dictionary<int, int> cachedCurrencies)
|
||||||
{
|
{
|
||||||
foreach (TraitEntryPacket entry in traitConfig.Entries)
|
foreach (TraitEntryPacket entry in traitEntries)
|
||||||
FillSpentCurrenciesMap(entry, cachedCurrencies);
|
AddSpentCurrenciesForEntry(entry, cachedCurrencies, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int[] GetClassAndSpecTreeCurrencies(TraitConfigPacket traitConfig)
|
public static int[] GetClassAndSpecTreeCurrencies(TraitConfigPacket traitConfig)
|
||||||
@@ -588,7 +588,7 @@ namespace Game
|
|||||||
if (cachedCurrencies == null)
|
if (cachedCurrencies == null)
|
||||||
{
|
{
|
||||||
cachedCurrencies = new();
|
cachedCurrencies = new();
|
||||||
FillSpentCurrenciesMap(traitConfig, cachedCurrencies);
|
FillSpentCurrenciesMap(traitConfig.Entries, cachedCurrencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (condition.TraitNodeGroupID != 0 || condition.TraitNodeID != 0 || condition.TraitNodeEntryID != 0)
|
if (condition.TraitNodeGroupID != 0 || condition.TraitNodeID != 0 || condition.TraitNodeEntryID != 0)
|
||||||
@@ -605,6 +605,68 @@ namespace Game
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool NodeMeetsTraitConditions(TraitConfigPacket traitConfig, Node node, uint traitNodeEntryId, Player player, Dictionary<int, int> spentCurrencies)
|
||||||
|
{
|
||||||
|
var meetsConditions = (bool isSufficient, bool hasFailedConditions) (List<TraitCondRecord> 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<TraitEntry> GetGrantedTraitEntriesForConfig(TraitConfigPacket traitConfig, Player player)
|
public static List<TraitEntry> GetGrantedTraitEntriesForConfig(TraitConfigPacket traitConfig, Player player)
|
||||||
{
|
{
|
||||||
List<TraitEntry> entries = new();
|
List<TraitEntry> entries = new();
|
||||||
@@ -701,24 +763,7 @@ namespace Game
|
|||||||
};
|
};
|
||||||
|
|
||||||
Dictionary<int, int> spentCurrencies = new();
|
Dictionary<int, int> spentCurrencies = new();
|
||||||
FillSpentCurrenciesMap(traitConfig, spentCurrencies);
|
FillSpentCurrenciesMap(traitConfig.Entries, spentCurrencies);
|
||||||
|
|
||||||
bool meetsConditions(List<TraitCondRecord> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isValidTraitEntry = TalentLearnResult (TraitEntryPacket traitEntry) =>
|
var isValidTraitEntry = TalentLearnResult (TraitEntryPacket traitEntry) =>
|
||||||
{
|
{
|
||||||
@@ -730,15 +775,7 @@ namespace Game
|
|||||||
if (getNodeEntryCount(traitEntry.TraitNodeID) != 1)
|
if (getNodeEntryCount(traitEntry.TraitNodeID) != 1)
|
||||||
return TalentLearnResult.FailedUnknown;
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
foreach (NodeEntry entry in node.Entries)
|
if (!NodeMeetsTraitConditions(traitConfig, node, (uint)traitEntry.TraitNodeEntryID, player, spentCurrencies))
|
||||||
if (entry.Data.Id == traitEntry.TraitNodeEntryID && !meetsConditions(entry.Conditions))
|
|
||||||
return TalentLearnResult.FailedUnknown;
|
|
||||||
|
|
||||||
if (!meetsConditions(node.Conditions))
|
|
||||||
return TalentLearnResult.FailedUnknown;
|
|
||||||
|
|
||||||
foreach (NodeGroup group in node.Groups)
|
|
||||||
if (!meetsConditions(group.Conditions))
|
|
||||||
return TalentLearnResult.FailedUnknown;
|
return TalentLearnResult.FailedUnknown;
|
||||||
|
|
||||||
if (!node.ParentNodes.Empty())
|
if (!node.ParentNodes.Empty())
|
||||||
@@ -772,6 +809,8 @@ namespace Game
|
|||||||
if (!removeInvalidEntries)
|
if (!removeInvalidEntries)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
AddSpentCurrenciesForEntry(traitConfig.Entries[i], spentCurrencies, -1);
|
||||||
|
|
||||||
if (traitConfig.Entries[i].GrantedRanks == 0 // fully remove entries that don't have granted ranks
|
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[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);
|
traitConfig.Entries.RemoveAt(i);
|
||||||
|
|||||||
Reference in New Issue
Block a user