From bb2df1c0f9e1b6a066c8b000c07be20afa53ad14 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Thu, 1 Feb 2024 15:39:38 -0500 Subject: [PATCH] Core/Spells: Named and implemented most of SpellAttr7 Port From (https://github.com/TrinityCore/TrinityCore/commit/4e87bd7942d932225436940f62e26d48719a42dd) --- .../Framework/Constants/Spells/SpellConst.cs | 68 +++++------ Source/Game/Entities/Object/WorldObject.cs | 4 +- Source/Game/Entities/Pet.cs | 17 --- Source/Game/Entities/StatSystem.cs | 4 +- Source/Game/Entities/Unit/Unit.Spells.cs | 49 +++++--- Source/Game/Entities/Unit/Unit.cs | 4 +- Source/Game/Spells/Auras/Aura.cs | 14 +++ Source/Game/Spells/Auras/AuraEffect.cs | 8 +- Source/Game/Spells/Spell.cs | 111 ++++++++++-------- Source/Game/Spells/SpellEffects.cs | 6 +- Source/Game/Spells/SpellInfo.cs | 2 +- 11 files changed, 151 insertions(+), 136 deletions(-) diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index f486683cb..fd2de3f97 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -1892,38 +1892,38 @@ namespace Framework.Constants } public enum SpellAttr7 : uint { - Unk0 = 0x01, // 0 Shaman'S New Spells (Call Of The ...), Feign Death. - IgnoreDurationMods = 0x02, // 1 Duration is not affected by duration modifiers - DisableAuraWhileDead = 0x04, // 2 Disable Aura While Dead - IsCheatSpell = 0x08, // 3 Cannot Cast If Caster Doesn'T Have Unitflag2 & UnitFlag2AllowCheatSpells - Unk4 = 0x10, // 4 Only 47883 (Soulstone Resurrection) And Test Spell. - SummonTotem = 0x20, // 5 Only Shaman Player Totems. - NoPushbackOnDamage = 0x40, // 6 Does not cause spell pushback on damage - Unk7 = 0x80, // 7 66218 (Launch) Spell. - HordeOnly = 0x100, // 8 Teleports, Mounts And Other Spells. - AllianceOnly = 0x200, // 9 Teleports, Mounts And Other Spells. - DispelCharges = 0x400, // 10 Dispel And Spellsteal Individual Charges Instead Of Whole Aura. - InterruptOnlyNonplayer = 0x800, // 11 Only Non-Player Casts Interrupt, Though Feral Charge - Bear Has It. - SilenceOnlyNonplayer = 0x1000, // 12 Not Set In 3.2.2a. - CanAlwaysBeInterrupted = 0x2000, // 13 Can always be interrupted, even if caster is immune - Unk14 = 0x4000, // 14 Only 52150 (Raise Dead - Pet) Spell. - Unk15 = 0x8000, // 15 Exorcism. Usable On Players? 100% Crit Chance On Undead And Demons? - HiddenInSpellbookWhenLearned = 0x10000, // 16 After learning these spells become hidden in spellbook (but are visible when not learned for low level characters) - Unk17 = 0x20000, // 17 Only 27965 (Suicide) Spell. - HasChargeEffect = 0x40000, // 18 Only Spells That Have Charge Among Effects. - ZoneTeleport = 0x80000, // 19 Teleports To Specific Zones. - Unk20 = 0x100000, // 20 Blink, Divine Shield, Ice Block - Unk21 = 0x200000, // 21 Not Set - Unk22 = 0x400000, // 22 - NoAttackDodge = 0x800000, // 23 No Attack Dodge - NoAttackParry = 0x1000000, // 24 No Attack Parry + AllowSpellReflection = 0x01, // Allow Spell Reflection + NoTargetDurationMod = 0x02, // No Target Duration Mod + DisableAuraWhileDead = 0x04, // Disable Aura While Dead + DebugSpell = 0x08, // Debug Spell Description Cannot Cast If Caster Doesn'T Have Unitflag2 & UnitFlag2AllowCheatSpells + TreatAsRaidBuff = 0x10, /*Nyi*/ // Treat As Raid Buff + CanBeMultiCast = 0x20, // Can Be Multi Cast + DontCauseSpellPushback = 0x40, // Don'T Cause Spell Pushback Description Damage Dealt By This Does Not Cause Spell Pushback + PrepareForVehicleControlEnd = 0x80, /*Nyi*/ // Prepare For Vehicle Control End + HordeSpecificSpell = 0x100, /*Nyi*/ // Horde Specific Spell + AllianceSpecificSpell = 0x200, /*Nyi*/ // Alliance Specific Spell + DispelRemovesCharges = 0x400, // Dispel Removes Charges Description Dispel/Spellsteal Remove Individual Charges + CanCauseInterrupt = 0x800, // Can Cause Interrupt Description Only Interrupt Non-Player Casting + CanCauseSilence = 0x1000, /*Nyi*/ // Can Cause Silence + NoUiNotInterruptible = 0x2000, // No Ui Not Interruptible Description Can Always Be Interrupted, Even If Caster Is Immune + RecastOnResummon = 0x4000, /*Nyi - Deprecated Attribute, There Is No SpellGo Sent Anymore On Pet Resummon*/ // Recast On Resummon + ResetSwingTimerAtSpellStart = 0x8000, // Reset Swing Timer At Spell Start + OnlyInSpellbookUntilLearned = 0x10000, // Only In Spellbook Until Learned Description After Learning These Spells Become Hidden In Spellbook (But Are Visible When Not Learned For Low Level Characters) + DoNotLogPvpKill = 0x20000, /*Nyi, Only Used By 1 Spell That Is Already Filtered Out In Pvp Credits Because Its Self Targeting*/ // Do Not Log Pvp Kill + AttackOnChargeToUnit = 0x40000, // Attack On Charge To Unit + ReportSpellFailureToUnitTarget = 0x80000, // Report Spell Failure To Unit Target + NoClientFailWhileStunnedFleeingConfused = 0x100000, // No Client Fail While Stunned, Fleeing, Confused Description Clientside - Skips Stunned/Fleeing/Confused Checks + RetainCooldownThroughLoad = 0x200000, /*Nyi*/ // Retain Cooldown Through Load + IgnoresColdWeatherFlyingRequirement = 0x400000, /*Nyi - Deprecated Attribute*/ // Ignores Cold Weather Flying Requirement + NoAttackDodge = 0x800000, // No Attack Dodge + NoAttackParry = 0x1000000, // No Attack Parry NoAttackMiss = 0x2000000, // No Attack Miss - Unk26 = 0x4000000, // 26 - BypassNoResurrectAura = 0x8000000, // 27 Bypass No Resurrect Aura - ConsolidatedRaidBuff = 0x10000000, // 28 Related To Player Positive Buff - Unk29 = 0x20000000, // 29 Only 69028, 71237 - Unk30 = 0x40000000, // 30 Burning Determination, Divine Sacrifice, Earth Shield, Prayer Of Mending - ClientIndicator = 0x80000000 // 31 Only 70769 + TreatAsNpcAoe = 0x4000000, // Treat As Npc Aoe + BypassNoResurrectAura = 0x8000000, // Bypass No Resurrect Aura + DoNotCountForPvpScoreboard = 0x10000000, // Do Not Count For Pvp Scoreboard + ReflectionOnlyDefends = 0x20000000, // Reflection Only Defends + CanProcFromSuppressedTargetProcs = 0x40000000, // Can Proc From Suppressed Target Procs + AlwaysCastLog = 0x80000000 // Always Cast Log } public enum SpellAttr8 : uint { @@ -2067,8 +2067,8 @@ namespace Framework.Constants } public enum SpellAttr12 : uint { - Unk0 = 0x01, // 0 - Unk1 = 0x02, // 1 + EnableProcsFromSuppressedCasterProcs = 0x01, // 0 + CanProcFromSuppressedCasterProcs = 0x02, // 1 Unk2 = 0x04, // 2 Unk3 = 0x08, // 3 Unk4 = 0x10, // 4 @@ -2595,7 +2595,7 @@ namespace Framework.Constants RangedMask = DealRangedAttack | TakeRangedAttack | DealRangedAbility | TakeRangedAbility, - SpellMask = DealMeleeAbility | TakeMeleeAbility | DealRangedAttack | TakeRangedAttack + SpellMask = DealMeleeAbility | TakeMeleeAbility | DealRangedAttack | TakeRangedAttack | DealRangedAbility | TakeRangedAbility | DealHelpfulAbility | TakeHelpfulAbility | DealHarmfulAbility | TakeHarmfulAbility | DealHelpfulSpell | TakeHelpfulSpell | DealHarmfulSpell | TakeHarmfulSpell | DealHarmfulPeriodic | TakeHarmfulPeriodic | DealHelpfulPeriodic | TakeHelpfulPeriodic, diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index e1a649788..e2a979d31 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -1917,7 +1917,7 @@ namespace Game.Entities return duration; // some auras are not affected by duration modifiers - if (spellInfo.HasAttribute(SpellAttr7.IgnoreDurationMods)) + if (spellInfo.HasAttribute(SpellAttr7.NoTargetDurationMod)) return duration; // cut duration only of negative effects @@ -2163,7 +2163,7 @@ namespace Game.Entities reflectchance += victim.GetTotalAuraModifierByMiscMask(AuraType.ReflectSpellsSchool, (int)spellInfo.GetSchoolMask()); if (reflectchance > 0 && RandomHelper.randChance(reflectchance)) - return SpellMissInfo.Reflect; + return spellInfo.HasAttribute(SpellAttr7.ReflectionOnlyDefends) ? SpellMissInfo.Deflect : SpellMissInfo.Reflect; } if (spellInfo.HasAttribute(SpellAttr3.AlwaysHit)) diff --git a/Source/Game/Entities/Pet.cs b/Source/Game/Entities/Pet.cs index 45ae18317..2b2963d32 100644 --- a/Source/Game/Entities/Pet.cs +++ b/Source/Game/Entities/Pet.cs @@ -295,23 +295,6 @@ namespace Game.Entities petStable.SetCurrentActivePetIndex((uint)activePetIndex); } - // Send fake summon spell cast - this is needed for correct cooldown application for spells - // Example: 46584 - without this cooldown (which should be set always when pet is loaded) isn't set clientside - // @todo pets should be summoned from real cast instead of just faking it? - if (petInfo.CreatedBySpellId != 0) - { - SpellGo spellGo = new(); - SpellCastData castData = spellGo.Cast; - - castData.CasterGUID = owner.GetGUID(); - castData.CasterUnit = owner.GetGUID(); - castData.CastID = ObjectGuid.Create(HighGuid.Cast, SpellCastSource.Normal, owner.GetMapId(), petInfo.CreatedBySpellId, map.GenerateLowGuid(HighGuid.Cast)); - castData.SpellID = (int)petInfo.CreatedBySpellId; - castData.CastFlags = SpellCastFlags.Unk9; - castData.CastTime = Time.GetMSTime(); - owner.SendMessageToSet(spellGo, true); - } - owner.SetMinion(this, true); if (!isTemporarySummon) diff --git a/Source/Game/Entities/StatSystem.cs b/Source/Game/Entities/StatSystem.cs index 6e29265e4..5392e2264 100644 --- a/Source/Game/Entities/StatSystem.cs +++ b/Source/Game/Entities/StatSystem.cs @@ -794,10 +794,10 @@ namespace Game.Entities damage -= (int)target.GetDamageReduction((uint)damage); } - public int CalculateAOEAvoidance(int damage, uint schoolMask, ObjectGuid casterGuid) + public int CalculateAOEAvoidance(int damage, uint schoolMask, bool npcCaster) { damage = (int)((float)damage * GetTotalAuraMultiplierByMiscMask(AuraType.ModAoeDamageAvoidance, schoolMask)); - if (casterGuid.IsAnyTypeCreature()) + if (npcCaster) damage = (int)((float)damage * GetTotalAuraMultiplierByMiscMask(AuraType.ModCreatureAoeDamageAvoidance, schoolMask)); return damage; diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index a8d7c9a88..1d7955db4 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -945,7 +945,7 @@ namespace Game.Entities public void SetSilencedSchoolMask(SpellSchoolMask schoolMask) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.SilencedSchoolMask), (uint)schoolMask); } public void ReplaceAllSilencedSchoolMask(SpellSchoolMask schoolMask) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.SilencedSchoolMask), (uint)schoolMask); } - + public SpellHistory GetSpellHistory() { return _spellHistory; } public static ProcFlagsHit CreateProcHitMask(SpellNonMeleeDamage damageInfo, SpellMissInfo missCondition) @@ -1511,10 +1511,18 @@ namespace Game.Entities } WeaponAttackType attType = damageInfo != null ? damageInfo.GetAttackType() : WeaponAttackType.BaseAttack; - if (typeMaskActor && actor != null) + SpellInfo spellInfo = null; + if (spell != null) + spellInfo = spell.GetSpellInfo(); + else if (damageInfo != null) + spellInfo = damageInfo.GetSpellInfo(); + else if (healInfo != null) + spellInfo = healInfo.GetSpellInfo(); + + if (typeMaskActor != null && actor != null && !(spellInfo != null && spellInfo.HasAttribute(SpellAttr3.SuppressCasterProcs))) actor.ProcSkillsAndReactives(false, actionTarget, typeMaskActor, hitMask, attType); - if (typeMaskActionTarget && actionTarget != null) + if (typeMaskActionTarget && actionTarget != null && !(spellInfo != null && spellInfo.HasAttribute(SpellAttr3.SuppressTargetProcs))) actionTarget.ProcSkillsAndReactives(true, actor, typeMaskActionTarget, hitMask, attType); if (actor != null) @@ -1903,9 +1911,12 @@ namespace Game.Entities Player bgPlayer = unit.ToPlayer(); if (bgPlayer != null) { - Battleground bg = bgPlayer.GetBattleground(); - if (bg != null) - bg.UpdatePlayerScore(bgPlayer, ScoreType.HealingDone, (uint)gain); + if (healInfo.GetSpellInfo() == null || !healInfo.GetSpellInfo().HasAttribute(SpellAttr7.DoNotCountForPvpScoreboard)) + { + Battleground bg = bgPlayer.GetBattleground(); + if (bg != null) + bg.UpdatePlayerScore(bgPlayer, ScoreType.HealingDone, (uint)gain); + } // use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria) if (gain != 0) @@ -2758,7 +2769,7 @@ namespace Game.Entities return false; } - + public bool HasNegativeAuraWithInterruptFlag(SpellAuraInterruptFlags flag, ObjectGuid guid = default) { if (!HasInterruptFlag(flag)) @@ -2806,7 +2817,7 @@ namespace Game.Entities return false; } - + public uint GetAuraCount(uint spellId) { uint count = 0; @@ -2873,7 +2884,7 @@ namespace Game.Entities // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) . 50% chance to dispell // Polymorph instead of 1 / (5 + 1) . 16%. - bool dispelCharges = aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelCharges); + bool dispelCharges = aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelRemovesCharges); byte charges = dispelCharges ? aura.GetCharges() : aura.GetStackAmount(); if (charges > 0) dispelList.Add(new DispelableAura(aura, chance, charges)); @@ -2911,7 +2922,7 @@ namespace Game.Entities { return false; } - + public void RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags flag, SpellInfo source = null) { if (!HasInterruptFlag(flag)) @@ -3038,7 +3049,7 @@ namespace Game.Entities } } - bool stealCharge = aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelCharges); + bool stealCharge = aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelRemovesCharges); // Cast duration to unsigned to prevent permanent aura's such as Righteous Fury being permanently added to caster uint dur = (uint)Math.Min(2u * Time.Minute * Time.InMilliseconds, aura.GetDuration()); @@ -3241,7 +3252,7 @@ namespace Game.Entities // Call OnDispel hook on AuraScript aura.CallScriptDispel(dispelInfo); - if (aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelCharges)) + if (aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelRemovesCharges)) aura.ModCharges(-dispelInfo.GetRemovedCharges(), AuraRemoveMode.EnemySpell); else aura.ModStackAmount(-dispelInfo.GetRemovedCharges(), AuraRemoveMode.EnemySpell); @@ -3455,7 +3466,7 @@ namespace Game.Entities // this may be a dead loop if some events on aura remove will continiously apply aura on remove // we want to have all auras removed, so use your brain when linking events for (int counter = 0; !m_appliedAuras.Empty() || !m_ownedAuras.Empty(); counter++) - { + { foreach (var aurAppIter in GetAppliedAuras()) _UnapplyAura(aurAppIter, AuraRemoveMode.Default); @@ -3815,7 +3826,7 @@ namespace Game.Entities } return null; } - + // spell mustn't have familyflags public AuraEffect GetAuraEffect(AuraType type, SpellFamilyNames family, FlagArray128 familyFlag, ObjectGuid casterGUID = default) { @@ -3903,7 +3914,7 @@ namespace Game.Entities AuraApplication aurApp = GetAuraApplication(predicate); return aurApp?.GetBase(); } - + public uint BuildAuraStateUpdateForTarget(Unit target) { uint auraStates = m_unitData.AuraState & ~(uint)AuraStateType.PerCasterAuraStateMask; @@ -3918,7 +3929,7 @@ namespace Game.Entities public bool CanProc() { return m_procDeep == 0; } public int GetProcChainLength() { return m_procChainLength; } - + public void _ApplyAuraEffect(Aura aura, uint effIndex) { Cypher.Assert(aura != null); @@ -3982,8 +3993,8 @@ namespace Game.Entities if (player != null) { if (Global.ConditionMgr.IsSpellUsedInSpellClickConditions(aurApp.GetBase().GetId())) - player.UpdateVisibleGameobjectsOrSpellClicks(); - + player.UpdateVisibleGameobjectsOrSpellClicks(); + player.FailCriteria(CriteriaFailEvent.GainAura, aurApp.GetBase().GetId()); player.StartCriteria(CriteriaStartEvent.GainAura, aurApp.GetBase().GetId()); player.UpdateCriteria(CriteriaType.GainAura, aurApp.GetBase().GetId()); @@ -4168,7 +4179,7 @@ namespace Game.Entities //uint removedAuras = m_removedAurasCount; RemoveAura(aurApp); //if (hasMoreThanOneEffect || m_removedAurasCount > removedAuras + 1) - //continue; + //continue; } } } diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index bbca9a781..6c602d219 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -2724,7 +2724,7 @@ namespace Game.Entities if (killer != null) { // in bg, count dmg if victim is also a player - if (victim.IsPlayer()) + if (victim.IsPlayer() && !(spellProto != null && spellProto.HasAttribute(SpellAttr7.DoNotCountForPvpScoreboard))) { Battleground bg = killer.GetBattleground(); if (bg != null) @@ -2864,7 +2864,7 @@ namespace Game.Entities if (damagetype != DamageEffectType.NoDamage && damagetype != DamageEffectType.DOT) { - if (victim != attacker && (spellProto == null || !(spellProto.HasAttribute(SpellAttr6.NoPushback) || spellProto.HasAttribute(SpellAttr7.NoPushbackOnDamage) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic)))) + if (victim != attacker && (spellProto == null || !(spellProto.HasAttribute(SpellAttr6.NoPushback) || spellProto.HasAttribute(SpellAttr7.DontCauseSpellPushback) || spellProto.HasAttribute(SpellAttr3.TreatAsPeriodic)))) { Spell spell = victim.GetCurrentSpell(CurrentSpellTypes.Generic); if (spell != null) diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index 778c43e30..026defaa3 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -1726,6 +1726,20 @@ namespace Game.Spells if (GetSpellInfo().HasAttribute(SpellAttr12.OnlyProcFromClassAbilities) && !spell.GetSpellInfo().HasAttribute(SpellAttr13.AllowClassAbilityProcs)) return 0; + + if (eventInfo.GetTypeMask().HasFlag(ProcFlags.TakenHitMask)) + { + if (spell.GetSpellInfo().HasAttribute(SpellAttr3.SuppressTargetProcs) + && !GetSpellInfo().HasAttribute(SpellAttr7.CanProcFromSuppressedTargetProcs)) + return 0; + } + else + { + if (spell.GetSpellInfo().HasAttribute(SpellAttr3.SuppressCasterProcs) + && !spell.GetSpellInfo().HasAttribute(SpellAttr12.EnableProcsFromSuppressedCasterProcs) + && !GetSpellInfo().HasAttribute(SpellAttr12.CanProcFromSuppressedCasterProcs)) + return 0; + } } // check don't break stealth attr present diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index d52970ee3..2c147d8c9 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -5126,8 +5126,8 @@ namespace Game.Spells if (!GetSpellInfo().HasAttribute(SpellAttr4.IgnoreDamageTakenModifiers)) { - if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) || GetSpellInfo().HasAttribute(SpellAttr5.TreatAsAreaEffect)) - damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, GetBase().GetCastItemGUID()); + if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) || GetSpellInfo().HasAttribute(SpellAttr5.TreatAsAreaEffect) || GetSpellInfo().HasAttribute(SpellAttr7.TreatAsNpcAoe)) + damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, (caster != null && !caster.IsControlledByPlayer()) || GetSpellInfo().HasAttribute(SpellAttr7.TreatAsNpcAoe)); } int dmg = (int)damage; @@ -5208,8 +5208,8 @@ namespace Game.Spells if (!GetSpellInfo().HasAttribute(SpellAttr4.IgnoreDamageTakenModifiers)) { - if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) || GetSpellInfo().HasAttribute(SpellAttr5.TreatAsAreaEffect)) - damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, GetBase().GetCastItemGUID()); + if (GetSpellEffectInfo().IsTargetingArea() || GetSpellEffectInfo().IsAreaAuraEffect() || GetSpellEffectInfo().IsEffect(SpellEffectName.PersistentAreaAura) || GetSpellInfo().HasAttribute(SpellAttr5.TreatAsAreaEffect) || GetSpellInfo().HasAttribute(SpellAttr7.TreatAsNpcAoe)) + damage = (uint)target.CalculateAOEAvoidance((int)damage, (uint)m_spellInfo.SchoolMask, (caster != null && !caster.IsControlledByPlayer()) || GetSpellInfo().HasAttribute(SpellAttr7.TreatAsNpcAoe)); } int dmg = (int)damage; diff --git a/Source/Game/Spells/Spell.cs b/Source/Game/Spells/Spell.cs index 4ed897e79..a408a647e 100644 --- a/Source/Game/Spells/Spell.cs +++ b/Source/Game/Spells/Spell.cs @@ -90,7 +90,7 @@ namespace Game.Spells // Determine if spell can be reflected back to the caster // Patch 1.2 notes: Spell Reflection no longer reflects abilities - m_canReflect = caster.IsUnit() && m_spellInfo.DmgClass == SpellDmgClass.Magic && !m_spellInfo.HasAttribute(SpellAttr0.IsAbility) + m_canReflect = caster.IsUnit() && ((m_spellInfo.DmgClass == SpellDmgClass.Magic && !m_spellInfo.HasAttribute(SpellAttr0.IsAbility)) || m_spellInfo.HasAttribute(SpellAttr7.AllowSpellReflection)) && !m_spellInfo.HasAttribute(SpellAttr1.NoReflection) && !m_spellInfo.HasAttribute(SpellAttr0.NoImmunities) && !m_spellInfo.IsPassive(); @@ -1746,9 +1746,9 @@ namespace Game.Spells if (m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster)) searchRadius = GetMinMaxRange(false).maxRange; else if (spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.ChainFromInitialTarget)) - searchRadius= jumpRadius; + searchRadius = jumpRadius; else - searchRadius= jumpRadius * chainTargets; + searchRadius = jumpRadius * chainTargets; WorldObject chainSource = m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) ? m_caster : target; List tempTargets = new(); @@ -1924,7 +1924,7 @@ namespace Game.Spells float hitDelay = m_spellInfo.LaunchDelay; WorldObject missileSource = m_caster; if (m_spellInfo.HasAttribute(SpellAttr4.BouncyChainMissiles)) - { + { var previousTargetInfo = m_UniqueTargetInfo.FindLast(target => (target.EffectMask & effectMask) != 0); if (previousTargetInfo != null) { @@ -2614,6 +2614,9 @@ namespace Game.Spells if (m_spellInfo.HasAttribute(SpellAttr12.StartCooldownOnCastStart)) SendSpellCooldown(); + if (m_spellInfo.HasAttribute(SpellAttr7.ResetSwingTimerAtSpellStart) && IsAutoActionResetSpell()) + ResetCombatTimers(); + //Containers for channeled spells have to be set // @todoApply this to all casted spells if needed // Why check duration? 29350: channelled triggers channelled @@ -2951,6 +2954,9 @@ namespace Game.Spells m_launchHandled = true; } + if (!m_spellInfo.HasAttribute(SpellAttr7.ResetSwingTimerAtSpellStart) && IsAutoActionResetSpell()) + ResetCombatTimers(); + // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()... SendSpellGo(); @@ -3052,8 +3058,7 @@ namespace Game.Spells if (!_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.IgnoreCastInProgress) && !m_spellInfo.HasAttribute(SpellAttr2.NotAnAction)) m_originalCaster.RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.ActionDelayed, m_spellInfo); - if (!m_spellInfo.HasAttribute(SpellAttr3.SuppressCasterProcs)) - Unit.ProcSkillsAndAuras(m_originalCaster, null, procAttacker, new ProcFlagsInit(ProcFlags.None), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Cast, hitMask, this, null, null); + Unit.ProcSkillsAndAuras(m_originalCaster, null, procAttacker, new ProcFlagsInit(ProcFlags.None), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.Cast, hitMask, this, null, null); // Call CreatureAI hook OnSpellCast Creature caster = m_originalCaster.ToCreature(); @@ -3306,8 +3311,7 @@ namespace Game.Spells } } - if (!m_spellInfo.HasAttribute(SpellAttr3.SuppressCasterProcs)) - Unit.ProcSkillsAndAuras(m_originalCaster, null, procAttacker, new ProcFlagsInit(ProcFlags.None), m_procSpellType, ProcFlagsSpellPhase.Finish, m_hitMask, this, null, null); + Unit.ProcSkillsAndAuras(m_originalCaster, null, procAttacker, new ProcFlagsInit(ProcFlags.None), m_procSpellType, ProcFlagsSpellPhase.Finish, m_hitMask, this, null, null); } void SendSpellCooldown() @@ -3452,8 +3456,7 @@ namespace Game.Spells if (creatureCaster != null) creatureCaster.ReleaseSpellFocus(this); - if (!m_spellInfo.HasAttribute(SpellAttr3.SuppressCasterProcs)) - Unit.ProcSkillsAndAuras(unitCaster, null, new ProcFlagsInit(ProcFlags.CastEnded), new ProcFlagsInit(), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, this, null, null); + Unit.ProcSkillsAndAuras(unitCaster, null, new ProcFlagsInit(ProcFlags.CastEnded), new ProcFlagsInit(), ProcFlagsSpellType.MaskAll, ProcFlagsSpellPhase.None, ProcFlagsHit.None, this, null, null); if (result != SpellCastResult.SpellCastOk) { @@ -3481,17 +3484,6 @@ namespace Game.Spells } } - if (IsAutoActionResetSpell()) - { - if (!m_spellInfo.HasAttribute(SpellAttr2.DoNotResetCombatTimers)) - { - unitCaster.ResetAttackTimer(WeaponAttackType.BaseAttack); - if (unitCaster.HaveOffhandWeapon()) - unitCaster.ResetAttackTimer(WeaponAttackType.OffAttack); - unitCaster.ResetAttackTimer(WeaponAttackType.RangedAttack); - } - } - // potions disabled by client, send event "not in combat" if need if (unitCaster.IsTypeId(TypeId.Player)) { @@ -3675,7 +3667,7 @@ namespace Game.Spells uint itemid = (uint)spellInfo.Reagent[i]; uint itemcount = spellInfo.ReagentCount[i]; - if (!caster.HasItemCount(itemid, itemcount)) + if (caster != null && !caster.HasItemCount(itemid, itemcount)) { packet.FailedArg1 = (int)itemid; // first missing item break; @@ -3689,7 +3681,7 @@ namespace Game.Spells { foreach (var reagentsCurrency in spellInfo.ReagentsCurrency) { - if (!caster.HasCurrency(reagentsCurrency.CurrencyTypesID, reagentsCurrency.CurrencyCount)) + if (caster != null && !caster.HasCurrency(reagentsCurrency.CurrencyTypesID, reagentsCurrency.CurrencyCount)) { packet.FailedArg1 = -1; packet.FailedArg2 = reagentsCurrency.CurrencyTypesID; @@ -3716,10 +3708,18 @@ namespace Game.Spells if (result == SpellCastResult.SpellCastOk) return; - if (!m_caster.IsTypeId(TypeId.Player)) + Player receiver = m_caster.ToPlayer(); + if (m_spellInfo.HasAttribute(SpellAttr7.ReportSpellFailureToUnitTarget)) + { + Player target = m_targets.GetUnitTarget()?.ToPlayer(); + if (target != null) + receiver = target; + } + + if (receiver == null) return; - if (m_caster.ToPlayer().IsLoading()) // don't send cast results at loading time + if (receiver.IsLoading()) // don't send cast results at loading time return; if (_triggeredCastFlags.HasAnyFlag(TriggerCastFlags.DontReportCastError)) @@ -3728,7 +3728,7 @@ namespace Game.Spells CastFailed castFailed = new(); castFailed.Visual = m_SpellVisual; FillSpellCastFailedArgs(castFailed, m_castId, m_spellInfo, result, m_customError, param1, param2, m_caster.ToPlayer()); - m_caster.ToPlayer().SendPacket(castFailed); + receiver.SendPacket(castFailed); } public void SendPetCastResult(SpellCastResult result, int? param1 = null, int? param2 = null) @@ -4740,7 +4740,7 @@ namespace Game.Spells } } - if (m_spellInfo.HasAttribute(SpellAttr7.IsCheatSpell) && m_caster.IsUnit() && !m_caster.ToUnit().HasUnitFlag2(UnitFlags2.AllowCheatSpells)) + if (m_spellInfo.HasAttribute(SpellAttr7.DebugSpell) && m_caster.IsUnit() && !m_caster.ToUnit().HasUnitFlag2(UnitFlags2.AllowCheatSpells)) { m_customError = SpellCustomErrors.GmOnly; return SpellCastResult.CustomError; @@ -7113,7 +7113,7 @@ namespace Game.Spells if (target.GetGUID() != corpse.GetOwnerGUID()) return false; - + if (!corpse.HasCorpseDynamicFlag(CorpseDynFlags.Lootable)) return false; @@ -7178,7 +7178,7 @@ namespace Game.Spells bool IsAutoActionResetSpell() { - if (IsTriggered()) + if (IsTriggered() || m_spellInfo.HasAttribute(SpellAttr2.DoNotResetCombatTimers)) return false; if (m_casttime == 0 && m_spellInfo.HasAttribute(SpellAttr6.DoesntResetSwingTimerIfInstant)) @@ -7195,7 +7195,8 @@ namespace Game.Spells bool IsNeedSendToClient() { return m_SpellVisual.SpellXSpellVisualID != 0 || m_SpellVisual.ScriptVisualID != 0 || m_spellInfo.IsChanneled() || - m_spellInfo.HasAttribute(SpellAttr8.AuraSendAmount) || m_spellInfo.HasHitDelay() || (m_triggeredByAuraSpell == null && !IsTriggered()); + (m_spellInfo.HasAttribute(SpellAttr8.AuraSendAmount)) || m_spellInfo.HasHitDelay() || (m_triggeredByAuraSpell == null && !IsTriggered()) || + m_spellInfo.HasAttribute(SpellAttr7.AlwaysCastLog); } public Unit GetUnitCasterForEffectHandlers() @@ -7226,7 +7227,7 @@ namespace Game.Spells PrepareTargetProcessing(); - foreach (TargetInfo target in m_UniqueTargetInfo) + foreach (TargetInfo target in m_UniqueTargetInfo) PreprocessSpellLaunch(target); foreach (var spellEffectInfo in m_spellInfo.GetEffects()) @@ -7300,9 +7301,9 @@ namespace Game.Spells if (m_originalCaster != null && m_damage > 0) { bool isAoeTarget = spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SpellEffectName.PersistentAreaAura); - if (isAoeTarget || m_spellInfo.HasAttribute(SpellAttr5.TreatAsAreaEffect)) + if (isAoeTarget || m_spellInfo.HasAttribute(SpellAttr5.TreatAsAreaEffect) || m_spellInfo.HasAttribute(SpellAttr7.TreatAsNpcAoe)) { - m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_originalCaster.GetGUID()); + m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, !m_originalCaster.IsControlledByPlayer() || m_spellInfo.HasAttribute(SpellAttr7.TreatAsNpcAoe)); if (m_originalCaster.IsPlayer()) { @@ -7352,6 +7353,18 @@ namespace Game.Spells targetInfo.Healing += m_healing; } + void ResetCombatTimers() + { + Unit unitCaster = m_caster.ToUnit(); + if (unitCaster == null) + return; + + unitCaster.ResetAttackTimer(WeaponAttackType.BaseAttack); + if (unitCaster.HaveOffhandWeapon()) + unitCaster.ResetAttackTimer(WeaponAttackType.OffAttack); + unitCaster.ResetAttackTimer(WeaponAttackType.RangedAttack); + } + SpellCastResult CanOpenLock(SpellEffectInfo effect, uint lockId, ref SkillType skillId, ref int reqSkillValue, ref int skillValue) { if (lockId == 0) // possible case for GO and maybe for items. @@ -7700,7 +7713,7 @@ namespace Game.Spells script._FinishScriptCall(); } } - + void CallScriptObjectAreaTargetSelectHandlers(List targets, uint effIndex, SpellImplicitTargetInfo targetType) { foreach (var script in m_loadedScripts) @@ -7782,7 +7795,7 @@ namespace Game.Spells return source.IsWithinLOS(target.GetPositionX(), target.GetPositionY(), target.GetPositionZ(), LineOfSightChecks.All, ignoreFlags); } - + bool CheckScriptEffectImplicitTargets(uint effIndex, uint effIndexToCheck) { // Skip if there are not any script @@ -8033,7 +8046,7 @@ namespace Game.Spells public bool IsChannelActive() { return m_caster.IsUnit() && m_caster.ToUnit().GetChannelSpellId() != 0; } public int GetProcChainLength() { return m_procChainLength; } - + public bool IsDeletable() { return !m_referencedFromCurrentSpell && !m_executedCurrently; @@ -8090,7 +8103,7 @@ namespace Game.Spells public SpellInfo GetTriggeredByAuraSpell() { return m_triggeredByAuraSpell; } public int GetTimer() { return m_timer; } - + #region Fields Dictionary _executeLogEffects = new(); PathGenerator m_preGeneratedPath; @@ -8499,7 +8512,7 @@ namespace Game.Spells ProcFlagsHit hitMask = ProcFlagsHit.None; // Spells with this flag cannot trigger if effect is cast on self - bool canEffectTrigger = (!spell.m_spellInfo.HasAttribute(SpellAttr3.SuppressCasterProcs) || !spell.m_spellInfo.HasAttribute(SpellAttr3.SuppressTargetProcs)) && spell.unitTarget.CanProc(); + bool canEffectTrigger = spell.unitTarget.CanProc(); // Trigger info was not filled in Spell::prepareDataForTriggerSystem - we do it now if (canEffectTrigger && !procAttacker && !procVictim) @@ -8667,12 +8680,6 @@ namespace Game.Spells // Do triggers for unit if (canEffectTrigger) { - if (spell.m_spellInfo.HasAttribute(SpellAttr3.SuppressCasterProcs)) - procAttacker = new ProcFlagsInit(); - - if (spell.m_spellInfo.HasAttribute(SpellAttr3.SuppressTargetProcs)) - procVictim = new ProcFlagsInit(); - 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) @@ -8709,7 +8716,7 @@ namespace Game.Spells } // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER - if (MissCondition == SpellMissInfo.None && spell.m_spellInfo.HasAttribute(SpellAttr7.InterruptOnlyNonplayer) && !unit.IsPlayer()) + if (MissCondition == SpellMissInfo.None && spell.m_spellInfo.HasAttribute(SpellAttr7.CanCauseInterrupt) && !unit.IsPlayer()) caster.CastSpell(unit, 32747, new CastSpellExtraArgs(spell)); } @@ -9256,7 +9263,7 @@ namespace Game.Spells Spell m_Spell; } - + class ProcReflectDelayed : BasicEvent { public ProcReflectDelayed(Unit owner, ObjectGuid casterGuid) @@ -9333,7 +9340,7 @@ namespace Game.Spells Targets = new(); Targets.SetDst(dest); } - + public CastSpellTargetArg(SpellCastTargets targets) { Targets = new(); @@ -9482,15 +9489,15 @@ namespace Game.Spells } public static implicit operator CastSpellExtraArgs(bool triggered) => new CastSpellExtraArgs(triggered); - + public static implicit operator CastSpellExtraArgs(TriggerCastFlags trigger) => new CastSpellExtraArgs(trigger); - + public static implicit operator CastSpellExtraArgs(Item item) => new CastSpellExtraArgs(item); - + public static implicit operator CastSpellExtraArgs(Spell triggeringSpell) => new CastSpellExtraArgs(triggeringSpell); - + public static implicit operator CastSpellExtraArgs(AuraEffect eff) => new CastSpellExtraArgs(eff); - + public static implicit operator CastSpellExtraArgs(Difficulty castDifficulty) => new CastSpellExtraArgs(castDifficulty); } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index a09d220c6..8c917ff48 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -3597,7 +3597,7 @@ namespace Game.Spells if (effectHandleMode == SpellEffectHandleMode.HitTarget) { // not all charge effects used in negative spells - if (!m_spellInfo.IsPositive() && m_caster.IsTypeId(TypeId.Player)) + if (m_spellInfo.HasAttribute(SpellAttr7.AttackOnChargeToUnit)) unitCaster.Attack(unitTarget, true); if (effectInfo.TriggerSpell != 0) @@ -4389,7 +4389,7 @@ namespace Game.Spells // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) . 50% chance to dispell // Polymorph instead of 1 / (5 + 1) . 16%. - bool dispelCharges = aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelCharges); + bool dispelCharges = aura.GetSpellInfo().HasAttribute(SpellAttr7.DispelRemovesCharges); byte charges = dispelCharges ? aura.GetCharges() : aura.GetStackAmount(); if (charges > 0) stealList.Add(new DispelableAura(aura, chance, charges)); @@ -4864,7 +4864,7 @@ namespace Game.Spells if (!player.HasSpell(spell_id) || player.GetSpellHistory().HasCooldown(spell_id)) continue; - if (!spellInfo.HasAttribute(SpellAttr9.SummonPlayerTotem)) + if (!spellInfo.HasAttribute(SpellAttr7.CanBeMultiCast)) continue; CastSpellExtraArgs args = new(TriggerCastFlags.IgnoreGCD | TriggerCastFlags.IgnoreCastInProgress | TriggerCastFlags.CastDirectly | TriggerCastFlags.DontReportCastError); diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index 3ecf5f9fa..720e134dd 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -3952,7 +3952,7 @@ namespace Game.Spells public bool CanBeInterrupted(WorldObject interruptCaster, Unit interruptTarget, bool ignoreImmunity = false) { - return HasAttribute(SpellAttr7.CanAlwaysBeInterrupted) + return HasAttribute(SpellAttr7.NoUiNotInterruptible) || HasChannelInterruptFlag(SpellAuraInterruptFlags.Damage | SpellAuraInterruptFlags.EnteringCombat) || (interruptTarget.IsPlayer() && InterruptFlags.HasFlag(SpellInterruptFlags.DamageCancelsPlayerOnly)) || InterruptFlags.HasFlag(SpellInterruptFlags.DamageCancels)