From 0f248e2faa48e4489b8a7f2faba39a325cc11573 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Fri, 24 Dec 2021 21:05:03 -0500 Subject: [PATCH] Core/SAI: allow creatures to handle gameobject spellhit SAI events. Port From (https://github.com/TrinityCore/TrinityCore/commit/83a9222c390110ae082512ed858a5580a958aa21) --- Source/Game/AI/SmartScripts/SmartAI.cs | 10 + Source/Game/Spells/Spell.cs | 316 ++++++++++++------------- 2 files changed, 168 insertions(+), 158 deletions(-) diff --git a/Source/Game/AI/SmartScripts/SmartAI.cs b/Source/Game/AI/SmartScripts/SmartAI.cs index e9e7ee1e4..b3027a617 100644 --- a/Source/Game/AI/SmartScripts/SmartAI.cs +++ b/Source/Game/AI/SmartScripts/SmartAI.cs @@ -662,11 +662,21 @@ namespace Game.AI GetScript().ProcessEventsFor(SmartEvents.SpellHit, caster, 0, 0, false, spellInfo); } + public override void SpellHitByGameObject(GameObject obj, SpellInfo spellInfo) + { + GetScript().ProcessEventsFor(SmartEvents.SpellHit, null, 0, 0, false, spellInfo, obj); + } + public override void SpellHitTarget(Unit target, SpellInfo spellInfo) { GetScript().ProcessEventsFor(SmartEvents.SpellHitTarget, target, 0, 0, false, spellInfo); } + public override void SpellHitTargetGameObject(GameObject target, SpellInfo spellInfo) + { + GetScript().ProcessEventsFor(SmartEvents.SpellHitTarget, null, 0, 0, false, spellInfo, target); + } + public override void DamageTaken(Unit attacker, ref uint damage) { GetScript().ProcessEventsFor(SmartEvents.Damaged, attacker, damage); diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 85a08e6a4..7a817f923 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -7862,193 +7862,193 @@ namespace Game.Spells // Get original caster (if exist) and calculate damage/healing from him data // Skip if m_originalCaster not available Unit caster = spell.GetOriginalCaster() ? spell.GetOriginalCaster() : spell.GetCaster().ToUnit(); - if (caster == null) - return; - - // Fill base trigger info - ProcFlags procAttacker = spell.m_procAttacker; - ProcFlags procVictim = spell.m_procVictim; - ProcFlagsSpellType procSpellType = ProcFlagsSpellType.None; - ProcFlagsHit hitMask = ProcFlagsHit.None; - - // Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = !spell.m_spellInfo.HasAttribute(SpellAttr3.CantTriggerProc) && spell.unitTarget.CanProc() && - (spell.CanExecuteTriggersOnHit(EffectMask) || MissCondition == SpellMissInfo.Immune || MissCondition == SpellMissInfo.Immune2); - - // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now - if (canEffectTrigger && procAttacker == 0 && procVictim == 0) + if (caster != null) { - bool positive = true; - if (spell.m_damage > 0) - positive = false; - else if (spell.m_healing == 0) - { - for (uint i = 0; i < SpellConst.MaxEffects; ++i) - { - // in case of immunity, check all effects to choose correct procFlags, as none has technically hit - if (EffectMask != 0 && (EffectMask & (1 << (int)i)) == 0) - continue; + // Fill base trigger info + ProcFlags procAttacker = spell.m_procAttacker; + ProcFlags procVictim = spell.m_procVictim; + ProcFlagsSpellType procSpellType = ProcFlagsSpellType.None; + ProcFlagsHit hitMask = ProcFlagsHit.None; - if (!spell.m_spellInfo.IsPositiveEffect(i)) + // Spells with this flag cannot trigger if effect is cast on self + bool canEffectTrigger = !spell.m_spellInfo.HasAttribute(SpellAttr3.CantTriggerProc) && spell.unitTarget.CanProc() && + (spell.CanExecuteTriggersOnHit(EffectMask) || MissCondition == SpellMissInfo.Immune || MissCondition == SpellMissInfo.Immune2); + + // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now + if (canEffectTrigger && procAttacker == 0 && procVictim == 0) + { + bool positive = true; + if (spell.m_damage > 0) + positive = false; + else if (spell.m_healing == 0) + { + for (uint i = 0; i < spell.m_spellInfo.GetEffects().Count; ++i) { - positive = false; - break; + // in case of immunity, check all effects to choose correct procFlags, as none has technically hit + if (EffectMask != 0 && (EffectMask & (1 << (int)i)) == 0) + continue; + + if (!spell.m_spellInfo.IsPositiveEffect(i)) + { + positive = false; + break; + } } } + + switch (spell.m_spellInfo.DmgClass) + { + case SpellDmgClass.Magic: + if (positive) + { + procAttacker |= ProcFlags.DoneSpellMagicDmgClassPos; + procVictim |= ProcFlags.TakenSpellMagicDmgClassPos; + } + else + { + procAttacker |= ProcFlags.DoneSpellMagicDmgClassNeg; + procVictim |= ProcFlags.TakenSpellMagicDmgClassNeg; + } + break; + case SpellDmgClass.None: + if (positive) + { + procAttacker |= ProcFlags.DoneSpellNoneDmgClassPos; + procVictim |= ProcFlags.TakenSpellNoneDmgClassPos; + } + else + { + procAttacker |= ProcFlags.DoneSpellNoneDmgClassNeg; + procVictim |= ProcFlags.TakenSpellNoneDmgClassNeg; + } + break; + } } - switch (spell.m_spellInfo.DmgClass) + // All calculated do it! + // Do healing + DamageInfo spellDamageInfo = null; + HealInfo healInfo = null; + if (spell.m_healing > 0) { - case SpellDmgClass.Magic: - if (positive) - { - procAttacker |= ProcFlags.DoneSpellMagicDmgClassPos; - procVictim |= ProcFlags.TakenSpellMagicDmgClassPos; - } - else - { - procAttacker |= ProcFlags.DoneSpellMagicDmgClassNeg; - procVictim |= ProcFlags.TakenSpellMagicDmgClassNeg; - } - break; - case SpellDmgClass.None: - if (positive) - { - procAttacker |= ProcFlags.DoneSpellNoneDmgClassPos; - procVictim |= ProcFlags.TakenSpellNoneDmgClassPos; - } - else - { - procAttacker |= ProcFlags.DoneSpellNoneDmgClassNeg; - procVictim |= ProcFlags.TakenSpellNoneDmgClassNeg; - } - break; + int addhealth = spell.m_healing; + if (IsCrit) + { + hitMask |= ProcFlagsHit.Critical; + addhealth = Unit.SpellCriticalHealingBonus(caster, spell.m_spellInfo, addhealth, null); + } + else + hitMask |= ProcFlagsHit.Normal; + + healInfo = new(caster, spell.unitTarget, (uint)addhealth, spell.m_spellInfo, spell.m_spellInfo.GetSchoolMask()); + caster.HealBySpell(healInfo, IsCrit); + spell.unitTarget.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, spell.m_spellInfo); + spell.m_healing = (int)healInfo.GetEffectiveHeal(); + + procSpellType |= ProcFlagsSpellType.Heal; } - } - // All calculated do it! - // Do healing - DamageInfo spellDamageInfo = null; - HealInfo healInfo = null; - if (spell.m_healing > 0) - { - int addhealth = spell.m_healing; - if (IsCrit) + // Do damage + if (spell.m_damage > 0) { - hitMask |= ProcFlagsHit.Critical; - addhealth = Unit.SpellCriticalHealingBonus(caster, spell.m_spellInfo, addhealth, null); + // Fill base damage struct (unitTarget - is real spell target) + SpellNonMeleeDamage damageInfo = new(caster, spell.unitTarget, spell.m_spellInfo, spell.m_SpellVisual, spell.m_spellSchoolMask, spell.m_castId); + // Check damage immunity + if (spell.unitTarget.IsImmunedToDamage(spell.m_spellInfo)) + { + hitMask = ProcFlagsHit.Immune; + spell.m_damage = 0; + + // no packet found in sniffs + } + else + { + // Add bonuses and fill damageInfo struct + caster.CalculateSpellDamageTaken(damageInfo, spell.m_damage, spell.m_spellInfo, spell.m_attackType, IsCrit); + Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); + + hitMask |= Unit.CreateProcHitMask(damageInfo, MissCondition); + procVictim |= ProcFlags.TakenDamage; + + spell.m_damage = (int)damageInfo.damage; + + caster.DealSpellDamage(damageInfo, true); + + // Send log damage message to client + caster.SendSpellNonMeleeDamageLog(damageInfo); + } + + // Do triggers for unit + if (canEffectTrigger) + { + spellDamageInfo = new(damageInfo, DamageEffectType.SpellDirect, spell.m_attackType, hitMask); + procSpellType |= ProcFlagsSpellType.Damage; + + if (caster.IsPlayer() && !spell.m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !spell.m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs) && + (spell.m_spellInfo.DmgClass == SpellDmgClass.Melee || spell.m_spellInfo.DmgClass == SpellDmgClass.Ranged)) + caster.ToPlayer().CastItemCombatSpell(spellDamageInfo); + } } - else - hitMask |= ProcFlagsHit.Normal; - healInfo = new(caster, spell.unitTarget, (uint)addhealth, spell.m_spellInfo, spell.m_spellInfo.GetSchoolMask()); - caster.HealBySpell(healInfo, IsCrit); - spell.unitTarget.GetThreatManager().ForwardThreatForAssistingMe(caster, healInfo.GetEffectiveHeal() * 0.5f, spell.m_spellInfo); - spell.m_healing = (int)healInfo.GetEffectiveHeal(); - - procSpellType |= ProcFlagsSpellType.Heal; - } - - // Do damage - if (spell.m_damage > 0) - { - // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo = new(caster, spell.unitTarget, spell.m_spellInfo, spell.m_SpellVisual, spell.m_spellSchoolMask, spell.m_castId); - // Check damage immunity - if (spell.unitTarget.IsImmunedToDamage(spell.m_spellInfo)) + // Passive spell hits/misses or active spells only misses (only triggers) + if (spell.m_damage <= 0 && spell.m_healing <= 0) { - hitMask = ProcFlagsHit.Immune; - spell.m_damage = 0; - - // no packet found in sniffs - } - else - { - // Add bonuses and fill damageInfo struct - caster.CalculateSpellDamageTaken(damageInfo, spell.m_damage, spell.m_spellInfo, spell.m_attackType, IsCrit); - Unit.DealDamageMods(damageInfo.attacker, damageInfo.target, ref damageInfo.damage, ref damageInfo.absorb); - + // Fill base damage struct (unitTarget - is real spell target) + SpellNonMeleeDamage damageInfo = new(caster, spell.unitTarget, spell.m_spellInfo, spell.m_SpellVisual, spell.m_spellSchoolMask); hitMask |= Unit.CreateProcHitMask(damageInfo, MissCondition); - procVictim |= ProcFlags.TakenDamage; + // Do triggers for unit + if (canEffectTrigger) + { + spellDamageInfo = new(damageInfo, DamageEffectType.NoDamage, spell.m_attackType, hitMask); + procSpellType |= ProcFlagsSpellType.NoDmgHeal; + } - spell.m_damage = (int)damageInfo.damage; - - caster.DealSpellDamage(damageInfo, true); - - // Send log damage message to client - caster.SendSpellNonMeleeDamageLog(damageInfo); + // Failed Pickpocket, reveal rogue + if (MissCondition == SpellMissInfo.Resist && spell.m_spellInfo.HasAttribute(SpellCustomAttributes.PickPocket) && spell.unitTarget.IsCreature()) + { + Unit unitCaster = spell.GetCaster().ToUnit(); + unitCaster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting); + spell.unitTarget.ToCreature().EngageWithTarget(unitCaster); + } } // Do triggers for unit if (canEffectTrigger) { - spellDamageInfo = new(damageInfo, DamageEffectType.SpellDirect, spell.m_attackType, hitMask); - procSpellType |= ProcFlagsSpellType.Damage; + Unit.ProcSkillsAndAuras(caster, spell.unitTarget, procAttacker, procVictim, procSpellType, ProcFlagsSpellPhase.Hit, hitMask, spell, spellDamageInfo, healInfo); - if (caster.IsPlayer() && !spell.m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !spell.m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs) && - (spell.m_spellInfo.DmgClass == SpellDmgClass.Melee || spell.m_spellInfo.DmgClass == SpellDmgClass.Ranged)) - caster.ToPlayer().CastItemCombatSpell(spellDamageInfo); - } - } - - // Passive spell hits/misses or active spells only misses (only triggers) - if (spell.m_damage <= 0 && spell.m_healing <= 0) - { - // Fill base damage struct (unitTarget - is real spell target) - SpellNonMeleeDamage damageInfo = new(caster, spell.unitTarget, spell.m_spellInfo, spell.m_SpellVisual, spell.m_spellSchoolMask); - hitMask |= Unit.CreateProcHitMask(damageInfo, MissCondition); - // Do triggers for unit - if (canEffectTrigger) - { - spellDamageInfo = new(damageInfo, DamageEffectType.NoDamage, spell.m_attackType, hitMask); - procSpellType |= ProcFlagsSpellType.NoDmgHeal; + // item spells (spell hit of non-damage spell may also activate items, for example seal of corruption hidden hit) + if (caster.IsPlayer() && procSpellType.HasAnyFlag(ProcFlagsSpellType.Damage | ProcFlagsSpellType.NoDmgHeal)) + { + if (spell.m_spellInfo.DmgClass == SpellDmgClass.Melee || spell.m_spellInfo.DmgClass == SpellDmgClass.Ranged) + if (!spell.m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !spell.m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs)) + caster.ToPlayer().CastItemCombatSpell(spellDamageInfo); + } } - // Failed Pickpocket, reveal rogue - if (MissCondition == SpellMissInfo.Resist && spell.m_spellInfo.HasAttribute(SpellCustomAttributes.PickPocket) && spell.unitTarget.IsCreature()) + // set hitmask for finish procs + spell.m_hitMask |= hitMask; + + // Do not take combo points on dodge and miss + if (MissCondition != SpellMissInfo.None && spell.m_needComboPoints && spell.m_targets.GetUnitTargetGUID() == TargetGUID) + spell.m_needComboPoints = false; + + // _spellHitTarget can be null if spell is missed in DoSpellHitOnUnit + if (MissCondition != SpellMissInfo.Evade && _spellHitTarget && !spell.GetCaster().IsFriendlyTo(unit) && (!spell.IsPositive() || spell.m_spellInfo.HasEffect(SpellEffectName.Dispel))) { Unit unitCaster = spell.GetCaster().ToUnit(); - unitCaster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting); - spell.unitTarget.ToCreature().EngageWithTarget(unitCaster); + if (unitCaster != null) + unitCaster.AtTargetAttacked(unit, spell.m_spellInfo.HasInitialAggro()); + + if (!unit.IsStandState()) + unit.SetStandState(UnitStandStateType.Stand); } + + // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER + if (MissCondition == SpellMissInfo.None && spell.m_spellInfo.HasAttribute(SpellAttr7.InterruptOnlyNonplayer) && !unit.IsPlayer()) + caster.CastSpell(unit, 32747, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(spell.m_castId)); } - // Do triggers for unit - if (canEffectTrigger) - { - Unit.ProcSkillsAndAuras(caster, spell.unitTarget, procAttacker, procVictim, procSpellType, ProcFlagsSpellPhase.Hit, hitMask, spell, spellDamageInfo, healInfo); - - // item spells (spell hit of non-damage spell may also activate items, for example seal of corruption hidden hit) - if (caster.IsPlayer() && procSpellType.HasAnyFlag(ProcFlagsSpellType.Damage | ProcFlagsSpellType.NoDmgHeal)) - { - if (spell.m_spellInfo.DmgClass == SpellDmgClass.Melee || spell.m_spellInfo.DmgClass == SpellDmgClass.Ranged) - if (!spell.m_spellInfo.HasAttribute(SpellAttr0.StopAttackTarget) && !spell.m_spellInfo.HasAttribute(SpellAttr4.SuppressWeaponProcs)) - caster.ToPlayer().CastItemCombatSpell(spellDamageInfo); - } - } - - // set hitmask for finish procs - spell.m_hitMask |= hitMask; - - // Do not take combo points on dodge and miss - if (MissCondition != SpellMissInfo.None && spell.m_needComboPoints && spell.m_targets.GetUnitTargetGUID() == TargetGUID) - spell.m_needComboPoints = false; - - // _spellHitTarget can be null if spell is missed in DoSpellHitOnUnit - if (MissCondition != SpellMissInfo.Evade && _spellHitTarget && !spell.GetCaster().IsFriendlyTo(unit) && (!spell.IsPositive() || spell.m_spellInfo.HasEffect(SpellEffectName.Dispel))) - { - Unit unitCaster = spell.GetCaster().ToUnit(); - if (unitCaster != null) - unitCaster.AtTargetAttacked(unit, spell.m_spellInfo.HasInitialAggro()); - - if (!unit.IsStandState()) - unit.SetStandState(UnitStandStateType.Stand); - } - - // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER - if (MissCondition == SpellMissInfo.None && spell.m_spellInfo.HasAttribute(SpellAttr7.InterruptOnlyNonplayer) && !unit.IsPlayer()) - caster.CastSpell(unit, 32747, new CastSpellExtraArgs(TriggerCastFlags.FullMask).SetOriginalCastId(spell.m_castId)); - if (_spellHitTarget) { //AI functions