diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index ccef62b0d..d54102e5f 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -2060,6 +2060,7 @@ namespace Framework.Constants NoInitialThreat = 0x10, AuraCC = 0x20, DontBreakStealth = 0x40, + CanCrit = 0x80, DirectDamage = 0x100, Charge = 0x200, PickPocket = 0x400, diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index aeb43d972..87c1bea4a 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -645,14 +645,15 @@ namespace Game.Entities return (uint)Math.Max(heal, 0.0f); } - public float SpellCritChanceDone(SpellInfo spellInfo, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack) + public float SpellCritChanceDone(Spell spell, AuraEffect aurEff, SpellSchoolMask schoolMask, WeaponAttackType attackType = WeaponAttackType.BaseAttack) { + SpellInfo spellInfo = spell != null ? spell.GetSpellInfo() : aurEff.GetSpellInfo(); //! Mobs can't crit with spells. (Except player controlled) if (IsCreature() && !GetSpellModOwner()) return 0.0f; // not critting spell - if (spellInfo.HasAttribute(SpellAttr2.CantCrit)) + if (spell != null && !spellInfo.HasAttribute(SpellCustomAttributes.CanCrit)) return 0.0f; float crit_chance = 0.0f; @@ -691,7 +692,7 @@ namespace Game.Entities { SpellInfo spellInfo = spell != null ? spell.GetSpellInfo() : aurEff.GetSpellInfo(); // not critting spell - if (spellInfo.HasAttribute(SpellAttr2.CantCrit)) + if (spell != null && !spellInfo.HasAttribute(SpellCustomAttributes.CanCrit)) return 0.0f; float crit_chance = doneChance; diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index 8ace7c48c..f847ea72f 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -355,19 +355,6 @@ namespace Game.Spells _effects[effect.EffectIndex] = new AuraEffect(this, effect, baseAmount != null ? baseAmount[effect.EffectIndex] : (int?)null, caster); } } - - public float CalcPeriodicCritChance(Unit caster) - { - if (!caster) - return 0.0f; - - Player modOwner = caster.GetSpellModOwner(); - if (modOwner == null) - return 0.0f; - - float critChance = modOwner.SpellCritChanceDone(GetSpellInfo(), GetSpellInfo().GetSchoolMask(), GetSpellInfo().GetAttackType()); - return Math.Max(0.0f, critChance); - } public Unit GetCaster() { diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 7903eb0e3..392e95116 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -446,7 +446,7 @@ namespace Game.Spells public float GetCritChanceFor(Unit caster, Unit target) { - return target.SpellCritChanceTaken(caster, null, this, GetSpellInfo().GetSchoolMask(), GetBase().CalcPeriodicCritChance(caster), GetSpellInfo().GetAttackType()); + return target.SpellCritChanceTaken(caster, null, this, GetSpellInfo().GetSchoolMask(), CalcPeriodicCritChance(caster), GetSpellInfo().GetAttackType()); } public bool IsAffectingSpell(SpellInfo spell) @@ -595,6 +595,11 @@ namespace Game.Spells return false; break; } + case AuraType.ModSpellCritChance: + // skip spells that can't crit + if (spellInfo == null || !spellInfo.HasAttribute(SpellCustomAttributes.CanCrit)) + return false; + break; default: break; } @@ -5380,6 +5385,27 @@ namespace Game.Spells caster.SendSpellNonMeleeDamageLog(damageInfo); } + bool CanPeriodicTickCrit() + { + if (GetSpellInfo().HasAttribute(SpellAttr2.CantCrit)) + return false; + + return true; + } + + float CalcPeriodicCritChance(Unit caster) + { + if (!caster || !CanPeriodicTickCrit()) + return 0.0f; + + Player modOwner = caster.GetSpellModOwner(); + if (!modOwner) + return 0.0f; + + float critChance = modOwner.SpellCritChanceDone(null, this, GetSpellInfo().GetSchoolMask(), GetSpellInfo().GetAttackType()); + return Math.Max(0.0f, critChance); + } + void HandleBreakableCCAuraProc(AuraApplication aurApp, ProcEventInfo eventInfo) { int damageLeft = (int)(GetAmount() - eventInfo.GetDamageInfo().GetDamage()); diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 92a93cb0e..d0041b1e2 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -6872,7 +6872,7 @@ namespace Game.Spells } } - float critChance = m_caster.SpellCritChanceDone(m_spellInfo, m_spellSchoolMask, m_attackType); + float critChance = m_caster.SpellCritChanceDone(this, null, m_spellSchoolMask, m_attackType); targetInfo.crit = RandomHelper.randChance(unit.SpellCritChanceTaken(m_caster, this, null, m_spellSchoolMask, critChance, m_attackType)); } diff --git a/Source/Game/Spells/SpellManager.cs b/Source/Game/Spells/SpellManager.cs index 65b9bf82c..1d82632b3 100644 --- a/Source/Game/Spells/SpellManager.cs +++ b/Source/Game/Spells/SpellManager.cs @@ -2661,6 +2661,23 @@ namespace Game.Entities break; } + switch (effect.Effect) + { + case SpellEffectName.SchoolDamage: + case SpellEffectName.HealthLeech: + case SpellEffectName.Heal: + case SpellEffectName.WeaponDamageNoSchool: + case SpellEffectName.WeaponPercentDamage: + case SpellEffectName.WeaponDamage: + case SpellEffectName.PowerBurn: + case SpellEffectName.HealMechanical: + case SpellEffectName.NormalizedWeaponDmg: + case SpellEffectName.HealPct: + case SpellEffectName.DamageFromMaxHealthPCT: + spellInfo.AttributesCu |= SpellCustomAttributes.CanCrit; + break; + } + switch (effect.Effect) { case SpellEffectName.SchoolDamage: @@ -2846,38 +2863,43 @@ namespace Game.Entities // addition for binary spells, ommit spells triggering other spells foreach (var spellInfo in mSpellInfoMap.Values) { - if (spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)) - continue; - - bool allNonBinary = true; - bool overrideAttr = false; - foreach (SpellEffectInfo effect in spellInfo.GetEffects()) + if (!spellInfo.HasAttribute(SpellCustomAttributes.BinarySpell)) { - if (effect == null) - continue; - - if (effect.IsAura() && effect.TriggerSpell != 0) + bool allNonBinary = true; + bool overrideAttr = false; + foreach (SpellEffectInfo effect in spellInfo.GetEffects()) { - switch (effect.ApplyAuraName) + if (effect == null) + continue; + + if (effect.IsAura() && effect.TriggerSpell != 0) { - case AuraType.PeriodicTriggerSpell: - case AuraType.PeriodicTriggerSpellWithValue: - SpellInfo triggerSpell = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, Difficulty.None); - if (triggerSpell != null) - { - overrideAttr = true; - if (triggerSpell.HasAttribute(SpellCustomAttributes.BinarySpell)) - allNonBinary = false; - } - break; - default: - break; + switch (effect.ApplyAuraName) + { + case AuraType.PeriodicTriggerSpell: + case AuraType.PeriodicTriggerSpellWithValue: + SpellInfo triggerSpell = Global.SpellMgr.GetSpellInfo(effect.TriggerSpell, Difficulty.None); + if (triggerSpell != null) + { + overrideAttr = true; + if (triggerSpell.HasAttribute(SpellCustomAttributes.BinarySpell)) + allNonBinary = false; + } + break; + default: + break; + } } } + + if (overrideAttr && allNonBinary) + spellInfo.AttributesCu &= ~SpellCustomAttributes.BinarySpell; } - if (overrideAttr && allNonBinary) - spellInfo.AttributesCu &= ~SpellCustomAttributes.BinarySpell; + // remove attribute from spells that can't crit + if (spellInfo.HasAttribute(SpellCustomAttributes.CanCrit)) + if (spellInfo.HasAttribute(SpellAttr2.CantCrit)) + spellInfo.AttributesCu &= ~SpellCustomAttributes.CanCrit; } Log.outInfo(LogFilter.ServerLoading, "Loaded spell custom attributes in {0} ms", Time.GetMSTimeDiffToNow(oldMSTime));