diff --git a/Source/Game/Entities/Unit/Unit.Combat.cs b/Source/Game/Entities/Unit/Unit.Combat.cs index f1670b3f8..f0808c878 100644 --- a/Source/Game/Entities/Unit/Unit.Combat.cs +++ b/Source/Game/Entities/Unit/Unit.Combat.cs @@ -1125,15 +1125,68 @@ namespace Game.Entities victim.ToCreature().LowerPlayerDamageReq(health < damage ? health : damage); } + bool killed = false; + bool skipSettingDeathState = false; + if (health <= damage) { + killed = true; + Log.outDebug(LogFilter.Unit, "DealDamage: victim just died"); if (victim.IsTypeId(TypeId.Player) && victim != this) victim.ToPlayer().UpdateCriteria(CriteriaTypes.TotalDamageReceived, health); - Kill(victim, durabilityLoss); + if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.Self && victim.HasAuraType(AuraType.SchoolAbsorbOverkill)) + { + var vAbsorbOverkill = victim.GetAuraEffectsByType(AuraType.SchoolAbsorbOverkill); + DamageInfo damageInfo = new(this, victim, damage, spellProto, damageSchoolMask, damagetype, cleanDamage != null ? cleanDamage.attackType : WeaponAttackType.BaseAttack); + + foreach (var absorbAurEff in vAbsorbOverkill) + { + Aura baseAura = absorbAurEff.GetBase(); + AuraApplication aurApp = baseAura.GetApplicationOfTarget(victim.GetGUID()); + if (aurApp == null) + continue; + + if ((absorbAurEff.GetMiscValue() & (int)damageInfo.GetSchoolMask()) == 0) + continue; + + // cannot absorb over limit + if (damage >= victim.CountPctFromMaxHealth(100 + absorbAurEff.GetMiscValueB())) + continue; + + // get amount which can be still absorbed by the aura + int currentAbsorb = absorbAurEff.GetAmount(); + // aura with infinite absorb amount - let the scripts handle absorbtion amount, set here to 0 for safety + if (currentAbsorb < 0) + currentAbsorb = 0; + + uint tempAbsorb = (uint)currentAbsorb; + + // This aura type is used both by Spirit of Redemption (death not really prevented, must grant all credit immediately) and Cheat Death (death prevented) + // repurpose PreventDefaultAction for this + bool deathFullyPrevented = false; + + absorbAurEff.GetBase().CallScriptEffectAbsorbHandlers(absorbAurEff, aurApp, damageInfo, ref tempAbsorb, ref deathFullyPrevented); + currentAbsorb = (int)tempAbsorb; + + // absorb must be smaller than the damage itself + currentAbsorb = MathFunctions.RoundToInterval(ref currentAbsorb, 0, (int)damageInfo.GetDamage()); + damageInfo.AbsorbDamage((uint)currentAbsorb); + + if (deathFullyPrevented) + killed = false; + + skipSettingDeathState = true; + } + + damage = damageInfo.GetDamage(); + } } + + if (killed) + Kill(victim, durabilityLoss, skipSettingDeathState); else { Log.outDebug(LogFilter.Unit, "DealDamageAlive"); @@ -1484,7 +1537,7 @@ namespace Game.Entities Cell.VisitWorldObjects(this, notifier, GetVisibilityRange()); } - public void Kill(Unit victim, bool durabilityLoss = true) + public void Kill(Unit victim, bool durabilityLoss = true, bool skipSettingDeathState = false) { // Prevent killing unit twice (and giving reward from kill twice) if (victim.GetHealth() == 0) @@ -1599,8 +1652,11 @@ namespace Game.Entities if (player != null) player.UpdateCriteria(CriteriaTypes.GetKillingBlows, 1, 0, 0, victim); - Log.outDebug(LogFilter.Unit, "SET JUST_DIED"); - victim.SetDeathState(DeathState.JustDied); + if (!skipSettingDeathState) + { + Log.outDebug(LogFilter.Unit, "SET JUST_DIED"); + victim.SetDeathState(DeathState.JustDied); + } // Inform pets (if any) when player kills target) // MUST come after victim.setDeathState(JUST_DIED); or pet next target @@ -1760,7 +1816,7 @@ namespace Game.Entities } } - public void KillSelf(bool durabilityLoss = true) { Kill(this, durabilityLoss); } + public void KillSelf(bool durabilityLoss = true, bool skipSettingDeathState = false) { Kill(this, durabilityLoss, skipSettingDeathState); } public virtual float GetBlockPercent(uint attackerLevel) { return 30.0f; } diff --git a/Source/Game/Scripting/SpellScript.cs b/Source/Game/Scripting/SpellScript.cs index f62744721..df353f01f 100644 --- a/Source/Game/Scripting/SpellScript.cs +++ b/Source/Game/Scripting/SpellScript.cs @@ -940,8 +940,8 @@ namespace Game.Scripting } public class EffectAbsorbHandler : EffectBase { - public EffectAbsorbHandler(AuraEffectAbsorbDelegate _pEffectHandlerScript, byte _effIndex) - : base(_effIndex, AuraType.SchoolAbsorb) + public EffectAbsorbHandler(AuraEffectAbsorbDelegate _pEffectHandlerScript, byte _effIndex, bool overKill = false) + : base(_effIndex, overKill ? AuraType.SchoolAbsorbOverkill : AuraType.SchoolAbsorb) { pEffectHandlerScript = _pEffectHandlerScript; } diff --git a/Source/Scripts/Spells/Priest.cs b/Source/Scripts/Spells/Priest.cs index 543dd2001..f32677c6e 100644 --- a/Source/Scripts/Spells/Priest.cs +++ b/Source/Scripts/Spells/Priest.cs @@ -615,19 +615,15 @@ namespace Scripts.Spells.Priest void HandleAbsorb(AuraEffect aurEff, DamageInfo dmgInfo, ref uint absorbAmount) { Unit target = GetTarget(); - if (dmgInfo.GetDamage() >= target.GetHealth()) - { - target.CastSpell(target, SpellIds.SpiritOfRedemption, new CastSpellExtraArgs(aurEff)); - target.SetFullHealth(); - return; - } + target.CastSpell(target, SpellIds.SpiritOfRedemption, new CastSpellExtraArgs(aurEff)); + target.SetFullHealth(); - PreventDefaultAction(); + absorbAmount = dmgInfo.GetDamage(); } public override void Register() { - OnEffectAbsorb.Add(new EffectAbsorbHandler(HandleAbsorb, 0)); + OnEffectAbsorb.Add(new EffectAbsorbHandler(HandleAbsorb, 0, true)); } }