diff --git a/Source/Framework/Constants/Spells/SpellAuraConst.cs b/Source/Framework/Constants/Spells/SpellAuraConst.cs index a4f61d5a5..0151772ac 100644 --- a/Source/Framework/Constants/Spells/SpellAuraConst.cs +++ b/Source/Framework/Constants/Spells/SpellAuraConst.cs @@ -492,7 +492,7 @@ namespace Framework.Constants TriggerSpellOnHealthPct = 468, ShowConfirmationPromptWithDifficulty = 469, Unk470 = 470, - ModVersatility = 471, // NYI + ModVersatility = 471, Unk472 = 472, PreventDurabilityLossFromCombat = 473, Unk474 = 474, diff --git a/Source/Game/Entities/Player/Player.Spells.cs b/Source/Game/Entities/Player/Player.Spells.cs index b835bd2a3..6b1b2a541 100644 --- a/Source/Game/Entities/Player/Player.Spells.cs +++ b/Source/Game/Entities/Player/Player.Spells.cs @@ -866,6 +866,12 @@ namespace Game.Entities ApplyRatingMod(CombatRating.Mastery, (int)enchant_amount, apply); Log.outDebug(LogFilter.Player, "+ {0} MASTERY", enchant_amount); break; + case ItemModType.Versatility: + ApplyRatingMod(CombatRating.VersatilityDamageDone, (int)enchant_amount, apply); + ApplyRatingMod(CombatRating.VersatilityHealingDone, (int)enchant_amount, apply); + ApplyRatingMod(CombatRating.VersatilityDamageTaken, (int)enchant_amount, apply); + Log.outDebug(LogFilter.Player, "+ {0} VERSATILITY", enchant_amount); + break; default: break; } diff --git a/Source/Game/Entities/StatSystem.cs b/Source/Game/Entities/StatSystem.cs index 4d4074b8d..e17a893ae 100644 --- a/Source/Game/Entities/StatSystem.cs +++ b/Source/Game/Entities/StatSystem.cs @@ -928,6 +928,7 @@ namespace Game.Entities UpdateRating(combatRating); } + public override void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, out float min_damage, out float max_damage) { UnitMods unitMod; @@ -956,6 +957,10 @@ namespace Game.Entities float weaponMinDamage = GetWeaponDamageRange(attType, WeaponDamageRange.MinDamage); float weaponMaxDamage = GetWeaponDamageRange(attType, WeaponDamageRange.MaxDamage); + float versaDmgMod = 1.0f; + + MathFunctions.AddPct(ref versaDmgMod, GetRatingBonusValue(CombatRating.VersatilityDamageDone) + (float)GetTotalAuraModifier(AuraType.ModVersatility)); + SpellShapeshiftFormRecord shapeshift = CliDB.SpellShapeshiftFormStorage.LookupByKey(GetShapeshiftForm()); if (shapeshift != null && shapeshift.CombatRoundTime != 0) { @@ -975,9 +980,10 @@ namespace Game.Entities weaponMaxDamage = SharedConst.BaseMaxDamage; } - min_damage = ((baseValue + weaponMinDamage) * basePct + totalValue) * totalPct; - max_damage = ((baseValue + weaponMaxDamage) * basePct + totalValue) * totalPct; + min_damage = ((baseValue + weaponMinDamage) * basePct + totalValue) * totalPct * versaDmgMod; + max_damage = ((baseValue + weaponMaxDamage) * basePct + totalValue) * totalPct * versaDmgMod; } + void UpdateAllCritPercentages() { float value = 5.0f; @@ -1296,6 +1302,12 @@ namespace Game.Entities case CombatRating.Mastery: UpdateMastery(); break; + case CombatRating.VersatilityDamageDone: + UpdateVersatilityDamageDone(); + break; + case CombatRating.VersatilityHealingDone: + UpdateHealingDonePercentMod(); + break; } } public void UpdateMastery() @@ -1334,6 +1346,29 @@ namespace Game.Entities } } + public void UpdateVersatilityDamageDone() + { + // No proof that CR_VERSATILITY_DAMAGE_DONE is allways = PLAYER_VERSATILITY + SetUInt32Value(PlayerFields.Versatility, GetUInt32Value(PlayerFields.CombatRating1 + (int)CombatRating.VersatilityDamageDone)); + + if (GetClass() == Class.Hunter) + UpdateDamagePhysical(WeaponAttackType.RangedAttack); + else + UpdateDamagePhysical(WeaponAttackType.BaseAttack); + } + + public void UpdateHealingDonePercentMod() + { + float value = 1.0f; + + MathFunctions.AddPct(ref value, GetRatingBonusValue(CombatRating.VersatilityHealingDone) + GetTotalAuraModifier(AuraType.ModVersatility)); + + foreach (AuraEffect auraEffect in GetAuraEffectsByType(AuraType.ModHealingDonePercent)) + MathFunctions.AddPct(ref value, auraEffect.GetAmount()); + + SetStatFloatValue(PlayerFields.ModHealingDonePct, value); + } + void UpdateArmorPenetration(int amount) { // Store Rating Value diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index 74d254c00..cffc5f00d 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -2782,6 +2782,15 @@ namespace Game.Entities else TakenTotalMod *= GetTotalAuraMultiplier(AuraType.ModRangedDamageTakenPct); + // Versatility + Player modOwner = GetSpellModOwner(); + if (modOwner) + { + // only 50% of SPELL_AURA_MOD_VERSATILITY for damage reduction + float versaBonus = modOwner.GetTotalAuraModifier(AuraType.ModVersatility) / 2.0f; + MathFunctions.AddPct(ref TakenTotalMod, -(modOwner.GetRatingBonusValue(CombatRating.VersatilityDamageTaken) + versaBonus)); + } + float tmpDamage = 0.0f; if (TakenTotalCasterMod != 0) diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 957de357b..1e6a9bcdb 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -180,6 +180,11 @@ namespace Game.Entities if (IsTypeId(TypeId.Unit) && !IsPet()) DoneTotalMod *= ToCreature().GetSpellDamageMod(ToCreature().GetCreatureTemplate().Rank); + // Versatility + Player modOwner = GetSpellModOwner(); + if (modOwner) + MathFunctions.AddPct(ref DoneTotalMod, modOwner.GetRatingBonusValue(CombatRating.VersatilityDamageDone) + modOwner.GetTotalAuraModifier(AuraType.ModVersatility)); + float maxModDamagePercentSchool = 0.0f; if (IsTypeId(TypeId.Player)) { @@ -270,6 +275,15 @@ namespace Game.Entities // get all auras from caster that allow the spell to ignore resistance (sanctified wrath) TakenTotalCasterMod += GetTotalAuraModifierByMiscMask(AuraType.ModIgnoreTargetResist, (int)spellProto.GetSchoolMask()); + // Versatility + Player modOwner = GetSpellModOwner(); + if (modOwner) + { + // only 50% of SPELL_AURA_MOD_VERSATILITY for damage reduction + float versaBonus = modOwner.GetTotalAuraModifier(AuraType.ModVersatility) / 2.0f; + MathFunctions.AddPct(ref TakenTotalMod, -(modOwner.GetRatingBonusValue(CombatRating.VersatilityDamageTaken) + versaBonus)); + } + // from positive and negative SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN // multiplicative bonus, for example Dispersion + Shadowform (0.10*0.85=0.085) TakenTotalMod *= GetTotalAuraMultiplierByMiscMask(AuraType.ModDamagePercentTaken, (uint)spellProto.GetSchoolMask()); @@ -291,7 +305,6 @@ namespace Game.Entities if (TakenAdvertisedBenefit != 0) { // level penalty still applied on Taken bonus - is it blizzlike? - Player modOwner = GetSpellModOwner(); if (modOwner) { coeff *= 100.0f; @@ -490,6 +503,9 @@ namespace Game.Entities if (spellProto.SpellFamilyName == SpellFamilyNames.Potion) return 1.0f; + if (IsPlayer()) + return GetFloatValue(PlayerFields.ModHealingDonePct); + float DoneTotalMod = 1.0f; // Healing done percent diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index c1307612f..e3a2a8a5f 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -3136,6 +3136,17 @@ namespace Game.Spells target.ToPlayer().UpdateSpellDamageAndHealingBonus(); } + [AuraEffectHandler(AuraType.ModHealingDonePercent)] + void HandleModHealingDonePct(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + { + if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask | AuraEffectHandleModes.Stat)) + return; + + Player player = aurApp.GetTarget().ToPlayer(); + if (player) + player.UpdateHealingDonePercentMod(); + } + [AuraEffectHandler(AuraType.ModTotalStatPercentage)] void HandleModTotalPercentStat(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) { @@ -3286,6 +3297,21 @@ namespace Game.Spells target.UpdateAttackPowerAndDamage(true); } + [AuraEffectHandler(AuraType.ModVersatility)] + void HandleModVersatilityByPct(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + { + if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask | AuraEffectHandleModes.Stat)) + return; + + Player target = aurApp.GetTarget().ToPlayer(); + if (target) + { + target.SetStatFloatValue(PlayerFields.VersatilityBonus, target.GetTotalAuraModifier(AuraType.ModVersatility)); + target.UpdateHealingDonePercentMod(); + target.UpdateVersatilityDamageDone(); + } + } + [AuraEffectHandler(AuraType.ModMaxPower)] void HandleAuraModMaxPower(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) {