diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index 7bb629852..c18ead6d2 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -2141,7 +2141,7 @@ namespace Game.Entities SendPacket(new SendUnlearnSpells()); } - public void LearnSpell(uint spellId, bool dependent, uint fromSkill = 0, bool suppressMessaging = false, int? traitDefinitionId = null) + public void LearnSpell(uint spellId, bool dependent, uint fromSkill = 0, bool suppressMessaging = false, PlayerSpellTrait trait = null) { PlayerSpell spell = m_spells.LookupByKey(spellId); @@ -2149,7 +2149,7 @@ namespace Game.Entities bool active = !disabled || spell.Active; bool favorite = spell != null ? spell.Favorite : false; - bool learning = AddSpell(spellId, active, true, dependent, false, false, fromSkill, favorite, traitDefinitionId); + bool learning = AddSpell(spellId, active, true, dependent, false, false, fromSkill, favorite, trait); // prevent duplicated entires in spell book, also not send if not in world (loading) if (learning && IsInWorld) @@ -2158,7 +2158,8 @@ namespace Game.Entities LearnedSpellInfo learnedSpellInfo = new(); learnedSpellInfo.SpellID = spellId; learnedSpellInfo.Favorite = favorite; - learnedSpellInfo.TraitDefinitionID = traitDefinitionId; + if (trait != null) + learnedSpellInfo.TraitDefinitionID = trait.DefinitionId; learnedSpells.SuppressMessaging = suppressMessaging; learnedSpells.ClientLearnedSpellData.Add(learnedSpellInfo); SendPacket(learnedSpells); @@ -2216,7 +2217,7 @@ namespace Game.Entities bool cur_active = pSpell.Active; bool cur_dependent = pSpell.Dependent; - int? traitDefinitionId = pSpell.TraitDefinitionId; + PlayerSpellTrait trait = pSpell.Trait; if (disabled) { @@ -2380,9 +2381,9 @@ namespace Game.Entities } } - if (traitDefinitionId.HasValue) + if (trait != null) { - var traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitDefinitionId.Value); + var traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(trait.DefinitionId); if (traitDefinition != null) RemoveOverrideSpell(traitDefinition.OverridesSpellID, spellId); } @@ -2480,7 +2481,7 @@ namespace Game.Entities return null; } - bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0, bool favorite = false, int? traitDefinitionId = null) + bool AddSpell(uint spellId, bool active, bool learning, bool dependent, bool disabled, bool loading = false, uint fromSkill = 0, bool favorite = false, PlayerSpellTrait? trait = null) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, Difficulty.None); if (spellInfo == null) @@ -2559,16 +2560,16 @@ namespace Game.Entities dependent_set = true; } - if (spell.TraitDefinitionId != traitDefinitionId) + if (spell.Trait != trait) { - if (spell.TraitDefinitionId.HasValue) + if (spell.Trait != null) { - TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(spell.TraitDefinitionId.Value); + TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(spell.Trait.DefinitionId); if (traitDefinition != null) - RemoveOverrideSpell((uint)traitDefinition.OverridesSpellID, spellId); + RemoveOverrideSpell(traitDefinition.OverridesSpellID, spellId); } - spell.TraitDefinitionId = traitDefinitionId; + spell.Trait = trait; } spell.Favorite = favorite; @@ -2655,8 +2656,7 @@ namespace Game.Entities newspell.Dependent = dependent; newspell.Disabled = disabled; newspell.Favorite = favorite; - if (traitDefinitionId.HasValue) - newspell.TraitDefinitionId = traitDefinitionId.Value; + newspell.Trait = trait; // replace spells in action bars and spellbook to bigger rank if only one spell rank must be accessible if (newspell.Active && !newspell.Disabled && spellInfo.IsRanked()) @@ -2725,51 +2725,14 @@ namespace Game.Entities if (castSpell) { - CastSpellExtraArgs args = new(TriggerCastFlags.FullMask); - - if (traitDefinitionId.HasValue) - { - TraitConfig traitConfig = GetTraitConfig((int)(uint)m_activePlayerData.ActiveCombatTraitConfigID); - if (traitConfig != null) - { - int traitEntryIndex = traitConfig.Entries.FindIndexIf(traitEntry => - { - return CliDB.TraitNodeEntryStorage.LookupByKey(traitEntry.TraitNodeEntryID)?.TraitDefinitionID == traitDefinitionId; - }); - - int rank = 0; - if (traitEntryIndex >= 0) - rank = traitConfig.Entries[traitEntryIndex].Rank + traitConfig.Entries[traitEntryIndex].GrantedRanks; - - if (rank > 0) - { - var traitDefinitionEffectPoints = TraitMgr.GetTraitDefinitionEffectPointModifiers(traitDefinitionId.Value); - if (traitDefinitionEffectPoints != null) - { - foreach (TraitDefinitionEffectPointsRecord traitDefinitionEffectPoint in traitDefinitionEffectPoints) - { - if (traitDefinitionEffectPoint.EffectIndex >= spellInfo.GetEffects().Count) - continue; - - float basePoints = Global.DB2Mgr.GetCurveValueAt((uint)traitDefinitionEffectPoint.CurveID, rank); - if (traitDefinitionEffectPoint.GetOperationType() == TraitPointsOperationType.Multiply) - basePoints *= spellInfo.GetEffect((uint)traitDefinitionEffectPoint.EffectIndex).CalcBaseValue(this, null, 0, -1); - - args.AddSpellMod(SpellValueMod.BasePoint0 + traitDefinitionEffectPoint.EffectIndex, (int)basePoints); - } - } - } - } - } - - CastSpell(this, spellId, args); + CastSpell(this, spellId, true); if (spellInfo.HasEffect(SpellEffectName.SkillStep)) return false; } - if (traitDefinitionId.HasValue) + if (trait != null) { - TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(traitDefinitionId.Value); + TraitDefinitionRecord traitDefinition = CliDB.TraitDefinitionStorage.LookupByKey(trait.DefinitionId); if (traitDefinition != null && traitDefinition.OverridesSpellID != 0) AddOverrideSpell(traitDefinition.OverridesSpellID, spellId); } @@ -4043,6 +4006,6 @@ namespace Game.Entities public bool Dependent; public bool Disabled; public bool Favorite; - public int? TraitDefinitionId; + public PlayerSpellTrait Trait; } } diff --git a/Source/Game/Entities/Player/Player.Talents.cs b/Source/Game/Entities/Player/Player.Talents.cs index 1869a0914..a9a540373 100644 --- a/Source/Game/Entities/Player/Player.Talents.cs +++ b/Source/Game/Entities/Player/Player.Talents.cs @@ -1014,9 +1014,8 @@ namespace Game.Entities List costEntries = new(); // apply new traits - for (var i = 0; i < newConfig.Entries.Count; ++i) + foreach (TraitEntryPacket newEntry in newConfig.Entries) { - TraitEntryPacket newEntry = newConfig.Entries[i]; int oldEntryIndex = editedConfig.Entries.FindIndexIf(ufEntry => ufEntry.TraitNodeID == newEntry.TraitNodeID && ufEntry.TraitNodeEntryID == newEntry.TraitNodeEntryID); if (oldEntryIndex < 0) { @@ -1033,7 +1032,10 @@ namespace Game.Entities AddDynamicUpdateFieldValue(newTraitConfig.ModifyValue(newTraitConfig.Entries), newUfEntry); if (applyTraits) + { + ApplyTraitEntry(newEntry.TraitNodeEntryID, 0, 0, false); ApplyTraitEntry(newUfEntry.TraitNodeEntryID, newUfEntry.Rank, 0, true); + } } else if (newEntry.Rank != editedConfig.Entries[oldEntryIndex].Rank || newEntry.GrantedRanks != editedConfig.Entries[oldEntryIndex].GrantedRanks) { @@ -1080,9 +1082,8 @@ namespace Game.Entities } } - for (int i = 0; i < newConfig.SubTrees.Count; ++i) + foreach (TraitSubTreeCachePacket newSubTree in newConfig.SubTrees) { - var newSubTree = newConfig.SubTrees[i]; int oldSubTreeIndex = editedConfig.SubTrees.FindIndexIf(ufSubTree => ufSubTree.TraitSubTreeID == newSubTree.TraitSubTreeID); List subTreeEntries = new(); @@ -1094,6 +1095,7 @@ namespace Game.Entities newUfEntry.Rank = newSubTree.Entries[j].Rank; newUfEntry.GrantedRanks = newSubTree.Entries[j].GrantedRanks; } + if (oldSubTreeIndex < 0) { TraitSubTreeCache newUfSubTree = new(); @@ -1182,13 +1184,24 @@ namespace Game.Entities if (traitDefinition.SpellID != 0) { + Cypher.Assert(traitNodeEntry.TraitDefinitionID <= 0xFFFFFF && rank + grantedRanks <= 0xFF); + if (apply) - LearnSpell(traitDefinition.SpellID, true, 0, false, traitNodeEntry.TraitDefinitionID); + LearnSpell(traitDefinition.SpellID, true, 0, false, new PlayerSpellTrait(traitNodeEntry.TraitDefinitionID, rank + grantedRanks)); else RemoveSpell(traitDefinition.SpellID); } } + public PlayerSpellTrait GetTraitInfoForSpell(uint spellId) + { + PlayerSpell spell = m_spells.LookupByKey(spellId); + if (spell != null) + return spell.Trait; + + return null; + } + public void SetTraitConfigUseStarterBuild(int traitConfigId, bool useStarterBuild) { int configIndex = m_activePlayerData.TraitConfigs.FindIndexIf(traitConfig => diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index be7fac2e1..50d0e9289 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -8068,4 +8068,28 @@ namespace Game.Entities public uint? InstanceId; public uint? LfgDungeonsId; } + + public class PlayerSpellTrait + { + public int DefinitionId; + public int Rank; + + public PlayerSpellTrait(int definitionId, int rank) + { + DefinitionId = definitionId; + Rank = rank; + } + + public override bool Equals(object obj) + { + return obj is PlayerSpellTrait trait && + DefinitionId == trait.DefinitionId && + Rank == trait.Rank; + } + + public override int GetHashCode() + { + return HashCode.Combine(DefinitionId, Rank); + } + } } \ No newline at end of file diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index 8c3704a31..ed794dc8c 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -4107,7 +4107,38 @@ namespace Game.Spells Unit casterUnit = null; if (caster != null) + { casterUnit = caster.ToUnit(); + Player playerCaster = caster.ToPlayer(); + if (playerCaster != null) + { + PlayerSpellTrait trait = playerCaster.GetTraitInfoForSpell(_spellInfo.Id); + if (trait != null) + { + var traitDefinitionEffectPoints = TraitMgr.GetTraitDefinitionEffectPointModifiers(trait.DefinitionId); + if (traitDefinitionEffectPoints != null) + { + var pointsOverride = traitDefinitionEffectPoints.Find(p => p.EffectIndex == EffectIndex); + if (pointsOverride != null) + { + float traitBasePoints = Global.DB2Mgr.GetCurveValueAt((uint)pointsOverride.CurveID, trait.Rank); + switch (pointsOverride.GetOperationType()) + { + case TraitPointsOperationType.Set: + value = traitBasePoints; + break; + case TraitPointsOperationType.Multiply: + value *= traitBasePoints; + break; + case TraitPointsOperationType.None: + default: + break; + } + } + } + } + } + } if (Scaling.Variance != 0) {