From ea4bc6c02afdcc699dcaf396c9c1b98c230c7c9e Mon Sep 17 00:00:00 2001 From: Shauren Date: Tue, 13 Sep 2022 14:36:32 +0200 Subject: [PATCH] Core/Spells: Rename SpellAttr2 to use official attribute names * Corrected implementation of SPELL_ATTR1_ALLOW_WHILE_STEALTHED * Implemented SPELL_ATTR2_RETAIN_ITEM_CAST * Implemented SPELL_ATTR2_ALLOW_WHILE_INVISIBLE * Implemented SPELL_ATTR0_PROC_FAILURE_BURNS_CHARGE * Implemented SPELL_ATTR2_PROC_COOLDOWN_ON_FAILURE * Implemented SPELL_ATTR2_NO_TARGET_PER_SECOND_COSTS * Implemented SPELL_ATTR2_DO_NOT_REPORT_SPELL_FAILURE * Implemented SPELL_ATTR1_REQUIRE_ALL_TARGETS * Implemented SPELL_ATTR2_CHAIN_FROM_CASTER * Implemented SPELL_ATTR2_NO_ACTIVE_PETS * Implemented SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY --- src/server/game/Entities/Object/Object.cpp | 15 ++- src/server/game/Entities/Player/Player.cpp | 6 +- src/server/game/Entities/Unit/Unit.cpp | 100 ++++++++++------ src/server/game/Entities/Unit/Unit.h | 2 +- src/server/game/Miscellaneous/SharedDefines.h | 72 ++++++------ .../game/Server/Packets/CombatLogPackets.cpp | 2 + .../game/Spells/Auras/SpellAuraEffects.cpp | 25 +--- src/server/game/Spells/Auras/SpellAuras.cpp | 52 ++++++--- src/server/game/Spells/Auras/SpellAuras.h | 4 +- src/server/game/Spells/Spell.cpp | 107 +++++++++++++----- src/server/game/Spells/SpellDefines.h | 9 ++ src/server/game/Spells/SpellEffects.cpp | 13 ++- src/server/game/Spells/SpellInfo.cpp | 25 ++-- src/server/game/Spells/SpellMgr.cpp | 22 ++-- src/server/scripts/Spells/spell_item.cpp | 3 +- 15 files changed, 282 insertions(+), 175 deletions(-) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index d1c080987c..1c8c8eb75b 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2546,7 +2546,7 @@ void WorldObject::ModSpellCastTime(SpellInfo const* spellInfo, int32& castTime, if (!(spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) castTime = int32(float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - else if (spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + else if (spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTO_REPEAT)) castTime = int32(float(castTime) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]); else if (spellInfo->SpellVisual[0] == 3881 && unitCaster->HasAura(67556)) // cooking with Chef Hat. castTime = 500; @@ -2571,7 +2571,7 @@ void WorldObject::ModSpellDurationTime(SpellInfo const* spellInfo, int32& durati if (!(spellInfo->HasAttribute(SPELL_ATTR0_IS_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_IS_TRADESKILL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) && ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT)) duration = int32(float(duration) * GetFloatValue(UNIT_MOD_CAST_SPEED)); - else if (spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + else if (spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTO_REPEAT)) duration = int32(float(duration) * unitCaster->m_modAttackSpeedPct[RANGED_ATTACK]); } @@ -2941,6 +2941,17 @@ SpellCastResult WorldObject::CastSpell(SpellCastTargets const& targets, uint32 s spell->SetSpellValue(pair.first, pair.second); spell->m_CastItem = args.CastItem; + + if (!spell->m_CastItem && info->HasAttribute(SPELL_ATTR2_RETAIN_ITEM_CAST)) + { + if (args.TriggeringSpell) + spell->m_CastItem = args.TriggeringSpell->m_CastItem; + else if (args.TriggeringAura && !args.TriggeringAura->GetBase()->GetCastItemGUID().IsEmpty()) + if (Unit const* auraCaster = args.TriggeringAura->GetCaster()) + if (Player const* triggeringAuraCaster = auraCaster->ToPlayer()) + spell->m_CastItem = triggeringAuraCaster->GetItemByGuid(args.TriggeringAura->GetBase()->GetCastItemGUID()); + } + return spell->prepare(targets, args.TriggeringAura); } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 451edbca17..88a9d9ca5b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3111,7 +3111,7 @@ bool Player::HandlePassiveSpellLearn(SpellInfo const* spellInfo) // talent dependent passives activated at form apply have proper stance data ShapeshiftForm form = GetShapeshiftForm(); bool need_cast = (!spellInfo->Stances || (form && (spellInfo->Stances & (UI64LIT(1) << (form - 1)))) || - (!form && spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT))); + (!form && spellInfo->HasAttribute(SPELL_ATTR2_ALLOW_WHILE_NOT_SHAPESHIFTED_CASTER_FORM))); // Check EquippedItemClass // passive spells which apply aura and have an item requirement are to be added manually, instead of casted @@ -5224,7 +5224,7 @@ bool Player::UpdateCraftSkill(SpellInfo const* spellInfo) uint32 SkillValue = GetPureSkillValue(_spell_idx->second->SkillLine); // Alchemy Discoveries here - if (spellInfo && spellInfo->Mechanic == MECHANIC_DISCOVERY) + if (spellInfo->Mechanic == MECHANIC_DISCOVERY) { if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->SkillLine, spellInfo->Id, this)) LearnSpell(discoveredSpell, false); @@ -23595,7 +23595,7 @@ void Player::ApplyEquipCooldown(Item* pItem) continue; if (Aura* itemAura = GetAura(effect.SpellID, GetGUID(), pItem->GetGUID())) - itemAura->AddProcCooldown(now + procEntry->Cooldown); + itemAura->AddProcCooldown(procEntry, now); continue; } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fe6bcfb7b5..ecb5618b7c 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -786,10 +786,10 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons if (spellProto) { if (!spellProto->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)) - victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Damage, spellProto->Id); + victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Damage, spellProto); } else - victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Damage, 0); + victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Damage); // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation @@ -923,7 +923,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons victim->ModifyHealth(-(int32)damage); if (damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE) - victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::NonPeriodicDamage, spellProto ? spellProto->Id : 0); + victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::NonPeriodicDamage, spellProto); if (victim->GetTypeId() != TYPEID_PLAYER) { @@ -2880,7 +2880,7 @@ bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAu { if (!skipInstant || m_currentSpells[CURRENT_GENERIC_SPELL]->GetCastTime()) { - if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS))) + if (!isAutoshoot || !(m_currentSpells[CURRENT_GENERIC_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS))) return true; } } @@ -2888,7 +2888,7 @@ bool Unit::IsNonMeleeSpellCast(bool withDelayed, bool skipChanneled, bool skipAu if (!skipChanneled && m_currentSpells[CURRENT_CHANNELED_SPELL] && (m_currentSpells[CURRENT_CHANNELED_SPELL]->getState() != SPELL_STATE_FINISHED)) { - if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS))) + if (!isAutoshoot || !(m_currentSpells[CURRENT_CHANNELED_SPELL]->m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS))) return true; } // autorepeat spells may be finished or delayed, but they are still considered cast @@ -3829,19 +3829,37 @@ void Unit::RemoveNotOwnLimitedTargetAuras(bool onPhaseChange /*= false*/) } template -bool IsInterruptFlagIgnoredForSpell(InterruptFlag /*flag*/, Unit const* /*unit*/, SpellInfo const* /*spellInfo*/) +bool IsInterruptFlagIgnoredForSpell(InterruptFlag /*flag*/, Unit const* /*unit*/, SpellInfo const* /*auraSpellInfo*/, SpellInfo const* /*interruptSource*/) { return false; } -template<> -bool IsInterruptFlagIgnoredForSpell(SpellAuraInterruptFlags flag, Unit const* unit, SpellInfo const* spellInfo) +bool IsInterruptFlagIgnoredForSpell(SpellAuraInterruptFlags flag, Unit const* unit, SpellInfo const* auraSpellInfo, SpellInfo const* interruptSource) { - return flag == SpellAuraInterruptFlags::Moving && unit->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, spellInfo); + switch (flag) + { + case SpellAuraInterruptFlags::Moving: + return unit->HasAuraTypeWithAffectMask(SPELL_AURA_CAST_WHILE_WALKING, auraSpellInfo); + case SpellAuraInterruptFlags::Action: + case SpellAuraInterruptFlags::ActionDelayed: + if (interruptSource) + { + if (interruptSource->HasAttribute(SPELL_ATTR1_ALLOW_WHILE_STEALTHED) && auraSpellInfo->HasAura(SPELL_AURA_MOD_STEALTH)) + return true; + + if (interruptSource->HasAttribute(SPELL_ATTR2_ALLOW_WHILE_INVISIBLE) && auraSpellInfo->HasAura(SPELL_AURA_MOD_INVISIBILITY)) + return true; + } + break; + default: + break; + } + + return false; } template -void Unit::RemoveAurasWithInterruptFlags(InterruptFlags flag, uint32 except) +void Unit::RemoveAurasWithInterruptFlags(InterruptFlags flag, SpellInfo const* source) { if (!HasInterruptFlag(flag)) return; @@ -3852,8 +3870,8 @@ void Unit::RemoveAurasWithInterruptFlags(InterruptFlags flag, uint32 except) Aura* aura = (*iter)->GetBase(); ++iter; if (aura->GetSpellInfo()->HasAuraInterruptFlag(flag) - && (!except || aura->GetId() != except) - && !IsInterruptFlagIgnoredForSpell(flag, this, aura->GetSpellInfo())) + && (!source || aura->GetId() != source->Id) + && !IsInterruptFlagIgnoredForSpell(flag, this, aura->GetSpellInfo(), source)) { uint32 removedAuras = m_removedAurasCount; RemoveAura(aura); @@ -3866,15 +3884,15 @@ void Unit::RemoveAurasWithInterruptFlags(InterruptFlags flag, uint32 except) if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) if (spell->getState() == SPELL_STATE_CASTING && spell->GetSpellInfo()->HasChannelInterruptFlag(flag) - && spell->GetSpellInfo()->Id != except - && !IsInterruptFlagIgnoredForSpell(flag, this, spell->GetSpellInfo())) + && (!source || spell->GetSpellInfo()->Id != source->Id) + && !IsInterruptFlagIgnoredForSpell(flag, this, spell->GetSpellInfo(), source)) InterruptNonMeleeSpells(false, 0, true); UpdateInterruptMask(); } -template TC_GAME_API void Unit::RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags flag, uint32 except); -template TC_GAME_API void Unit::RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2 flag, uint32 except); +template TC_GAME_API void Unit::RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags flag, SpellInfo const* source); +template TC_GAME_API void Unit::RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags2 flag, SpellInfo const* source); void Unit::RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID) { @@ -7324,7 +7342,7 @@ bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT)) return false; - if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) return false; if (uint32 schoolMask = spellInfo->GetSchoolMask()) @@ -7478,7 +7496,7 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Worl if (list.count(aura) > 0) return true; - if (!spellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { // Check for immune to application of harmful magical effects AuraEffectList const& immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL); @@ -10099,30 +10117,44 @@ void Unit::GetProcAurasTriggeredOnEvent(AuraApplicationProcContainer& aurasTrigg { std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + auto processAuraApplication = [&](AuraApplication* aurApp) + { + if (uint32 procEffectMask = aurApp->GetBase()->GetProcEffectMask(aurApp, eventInfo, now)) + { + aurApp->GetBase()->PrepareProcToTrigger(aurApp, eventInfo, now); + aurasTriggeringProc.emplace_back(procEffectMask, aurApp); + } + else + { + if (aurApp->GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR0_PROC_FAILURE_BURNS_CHARGE)) + { + if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(aurApp->GetBase()->GetSpellInfo()->Id)) + { + aurApp->GetBase()->PrepareProcChargeDrop(procEntry, eventInfo); + aurApp->GetBase()->ConsumeProcCharges(procEntry); + } + } + + if (aurApp->GetBase()->GetSpellInfo()->HasAttribute(SPELL_ATTR2_PROC_COOLDOWN_ON_FAILURE)) + if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(aurApp->GetBase()->GetSpellInfo()->Id)) + aurApp->GetBase()->AddProcCooldown(procEntry, now); + } + }; + // use provided list of auras which can proc if (procAuras) { for (AuraApplication* aurApp : *procAuras) { ASSERT(aurApp->GetTarget() == this); - if (uint8 procEffectMask = aurApp->GetBase()->GetProcEffectMask(aurApp, eventInfo, now)) - { - aurApp->GetBase()->PrepareProcToTrigger(aurApp, eventInfo, now); - aurasTriggeringProc.emplace_back(procEffectMask, aurApp); - } + processAuraApplication(aurApp); } } // or generate one on our own else { for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr != GetAppliedAuras().end(); ++itr) - { - if (uint8 procEffectMask = itr->second->GetBase()->GetProcEffectMask(itr->second, eventInfo, now)) - { - itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now); - aurasTriggeringProc.emplace_back(procEffectMask, itr->second); - } - } + processAuraApplication(itr->second); } } @@ -10169,12 +10201,8 @@ void Unit::TriggerAurasProcOnEvent(ProcEventInfo& eventInfo, AuraApplicationProc if (disableProcs) SetCantProc(true); - for (auto const& aurAppProc : aurasTriggeringProc) + for (auto const& [procEffectMask, aurApp] : aurasTriggeringProc) { - AuraApplication* aurApp; - uint8 procEffectMask; - std::tie(procEffectMask, aurApp) = aurAppProc; - if (aurApp->GetRemoveMode().HasAnyFlag()) continue; @@ -11934,7 +11962,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target) if (!spellInfo) return nullptr; - if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD)) + if (!target->IsAlive() && !spellInfo->IsPassive() && !spellInfo->HasAttribute(SPELL_ATTR2_ALLOW_DEAD_TARGET)) return nullptr; if (target->IsImmunedToSpell(spellInfo, this)) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 2802fc0412..35f89d2e3f 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1349,7 +1349,7 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID = ObjectGuid::Empty, Aura* except = nullptr, bool negative = true, bool positive = true); void RemoveNotOwnLimitedTargetAuras(bool onPhaseChange = false); template - void RemoveAurasWithInterruptFlags(InterruptFlags flag, uint32 except = 0); + void RemoveAurasWithInterruptFlags(InterruptFlags flag, SpellInfo const* source = nullptr); void RemoveAurasWithAttribute(uint32 flags); void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID); void RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveFlags removemode = AuraRemoveFlags::ByDefault, uint32 except = 0); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index ef2778c780..0fcfc860cc 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -353,7 +353,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = enum SpellAttr0 : uint32 { - SPELL_ATTR0_PROC_FAILURE_BURNS_CHARGE = 0x00000001, /*NYI*/ // Proc Failure Burns Charge + SPELL_ATTR0_PROC_FAILURE_BURNS_CHARGE = 0x00000001, /*NYI, proc failure includes missed roll*/ // TITLE Proc Failure Burns Charge SPELL_ATTR0_USES_RANGED_SLOT = 0x00000002, // Uses Ranged Slot DESCRIPTION Use ammo, ranged attack range modifiers, ranged haste, etc. SPELL_ATTR0_ON_NEXT_SWING_NO_DAMAGE = 0x00000004, // On Next Swing (No Damage) DESCRIPTION Both "on next swing" attributes have identical handling in server & client SPELL_ATTR0_DO_NOT_LOG_IMMUNE_MISSES = 0x00000008, // Do Not Log Immune Misses (client only) @@ -364,7 +364,7 @@ enum SpellAttr0 : uint32 SPELL_ATTR0_DO_NOT_LOG = 0x00000100, // Do Not Log (client only) DESCRIPTION Spell will not appear in combat logs SPELL_ATTR0_HELD_ITEM_ONLY = 0x00000200, // Held Item Only (client only) DESCRIPTION Client will automatically select main-hand item as cast target SPELL_ATTR0_ON_NEXT_SWING = 0x00000400, // On Next Swing DESCRIPTION Both "on next swing" attributes have identical handling in server & client - SPELL_ATTR0_WEARER_CASTS_PROC_TRIGGER = 0x00000800, /*NYI*/ // Wearer Casts Proc Trigger + SPELL_ATTR0_WEARER_CASTS_PROC_TRIGGER = 0x00000800, // TITLE Wearer Casts Proc Trigger DESCRIPTION Just a marker attribute to show auras that trigger another spell (either directly or with a script) SPELL_ATTR0_SERVER_ONLY = 0x00001000, // Server Only SPELL_ATTR0_ALLOW_ITEM_SPELL_IN_PVP = 0x00002000, // Allow Item Spell In PvP SPELL_ATTR0_ONLY_INDOORS = 0x00004000, // Only Indoors @@ -416,7 +416,7 @@ enum SpellAttr1 : uint32 SPELL_ATTR1_IGNORE_OWNERS_DEATH = 0x00800000, /*NYI*/ // Ignore Owner's Death SPELL_ATTR1_SPECIAL_SKILLUP = 0x01000000, // Special Skillup SPELL_ATTR1_AURA_STAYS_AFTER_COMBAT = 0x02000000, // Aura Stays After Combat - SPELL_ATTR1_REQUIRE_ALL_TARGETS = 0x04000000, /*NYI, UNK*/ // Require All Targets + SPELL_ATTR1_REQUIRE_ALL_TARGETS = 0x04000000, // TITLE Require All Targets SPELL_ATTR1_DISCOUNT_POWER_ON_MISS = 0x08000000, // Discount Power On Miss SPELL_ATTR1_NO_AURA_ICON = 0x10000000, // No Aura Icon (client only) SPELL_ATTR1_NAME_IN_CHANNEL_BAR = 0x20000000, // Name in Channel Bar (client only) @@ -424,40 +424,40 @@ enum SpellAttr1 : uint32 SPELL_ATTR1_CAST_WHEN_LEARNED = 0x80000000 // Cast When Learned }; -enum SpellAttr2 +enum SpellAttr2 : uint32 { - SPELL_ATTR2_CAN_TARGET_DEAD = 0x00000001, // 0 can target dead unit or corpse - SPELL_ATTR2_UNK1 = 0x00000002, // 1 vanish, shadowform, Ghost Wolf and other - SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS = 0x00000004, // 2 26368 4.0.1 dbc change - SPELL_ATTR2_UNK3 = 0x00000008, // 3 - SPELL_ATTR2_DISPLAY_IN_STANCE_BAR = 0x00000010, // 4 client displays icon in stance bar when learned, even if not shapeshift - SPELL_ATTR2_AUTOREPEAT_FLAG = 0x00000020, // 5 - SPELL_ATTR2_CANT_TARGET_TAPPED = 0x00000040, // 6 target must be tapped by caster - SPELL_ATTR2_DONT_REPORT_SPELL_FAILURE = 0x00000080, // 7 Does not send spell failure packets when the cast has failed - SPELL_ATTR2_UNK8 = 0x00000100, // 8 not set in 3.0.3 - SPELL_ATTR2_UNK9 = 0x00000200, // 9 - SPELL_ATTR2_UNK10 = 0x00000400, // 10 related to tame - SPELL_ATTR2_HEALTH_FUNNEL = 0x00000800, // 11 - SPELL_ATTR2_UNK12 = 0x00001000, // 12 Cleave, Heart Strike, Maul, Sunder Armor, Swipe - SPELL_ATTR2_PRESERVE_ENCHANT_IN_ARENA = 0x00002000, // 13 Items enchanted by spells with this flag preserve the enchant to arenas - SPELL_ATTR2_UNK14 = 0x00004000, // 14 - SPELL_ATTR2_UNK15 = 0x00008000, // 15 not set in 3.0.3 - SPELL_ATTR2_TAME_BEAST = 0x00010000, // 16 - SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS = 0x00020000, // 17 don't reset timers for melee autoattacks (swings) or ranged autoattacks (autoshoots) - SPELL_ATTR2_REQ_DEAD_PET = 0x00040000, // 18 Only Revive pet and Heart of the Pheonix - SPELL_ATTR2_NOT_NEED_SHAPESHIFT = 0x00080000, // 19 does not necessarly need shapeshift - SPELL_ATTR2_INITIATE_COMBAT_POST_CAST = 0x00100000, // 20 Initiate Combat Post-Cast (Enables Auto-Attack) - SPELL_ATTR2_DAMAGE_REDUCED_SHIELD = 0x00200000, // 21 for ice blocks, pala immunity buffs, priest absorb shields, but used also for other spells -> not sure! - SPELL_ATTR2_NO_INITIAL_THREAT = 0x00400000, // 22 No Initial Threat - SPELL_ATTR2_IS_ARCANE_CONCENTRATION = 0x00800000, // 23 Only mage Arcane Concentration have this flag - SPELL_ATTR2_UNK24 = 0x01000000, // 24 - SPELL_ATTR2_UNK25 = 0x02000000, // 25 - SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE = 0x04000000, // 26 unaffected by school immunity - SPELL_ATTR2_UNK27 = 0x08000000, // 27 - SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS = 0x10000000, // 28 doesnt break auras with SpellAuraInterruptFlags::Action and SpellAuraInterruptFlags::ActionDelayed - SPELL_ATTR2_CANT_CRIT = 0x20000000, // 29 Spell can't crit - SPELL_ATTR2_ACTIVE_THREAT = 0x40000000, // 30 Active Threat - SPELL_ATTR2_FOOD_BUFF = 0x80000000 // 31 Food or Drink Buff (like Well Fed) + SPELL_ATTR2_ALLOW_DEAD_TARGET = 0x00000001, // TITLE Allow Dead Target + SPELL_ATTR2_NO_SHAPESHIFT_UI = 0x00000002, // TITLE No shapeshift UI (client only) DESCRIPTION Does not replace action bar when shapeshifted + SPELL_ATTR2_IGNORE_LINE_OF_SIGHT = 0x00000004, // TITLE Ignore Line of Sight + SPELL_ATTR2_ALLOW_LOW_LEVEL_BUFF = 0x00000008, // TITLE Allow Low Level Buff + SPELL_ATTR2_USE_SHAPESHIFT_BAR = 0x00000010, // TITLE Use Shapeshift Bar (client only) + SPELL_ATTR2_AUTO_REPEAT = 0x00000020, // TITLE Auto Repeat + SPELL_ATTR2_CANNOT_CAST_ON_TAPPED = 0x00000040, // TITLE Cannot cast on tapped DESCRIPTION Can only target untapped units, or those tapped by caster + SPELL_ATTR2_DO_NOT_REPORT_SPELL_FAILURE = 0x00000080, // TITLE Do Not Report Spell Failure + SPELL_ATTR2_INCLUDE_IN_ADVANCED_COMBAT_LOG = 0x00000100, // TITLE Include In Advanced Combat Log (client only) DESCRIPTION Determines whether to include this aura in list of auras in SMSG_ENCOUNTER_START + SPELL_ATTR2_ALWAYS_CAST_AS_UNIT = 0x00000200, /*NYI, UNK*/ // TITLE Always Cast As Unit + SPELL_ATTR2_SPECIAL_TAMING_FLAG = 0x00000400, // TITLE Special Taming Flag DESCRIPTION Related to taming? + SPELL_ATTR2_NO_TARGET_PER_SECOND_COSTS = 0x00000800, // TITLE No Target Per-Second Costs + SPELL_ATTR2_CHAIN_FROM_CASTER = 0x00001000, // TITLE Chain From Caster + SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY = 0x00002000, // TITLE Enchant own item only + SPELL_ATTR2_ALLOW_WHILE_INVISIBLE = 0x00004000, // TITLE Allow While Invisible + SPELL_ATTR2_DO_NOT_CONSUME_IF_GAINED_DURING_CAST = 0x00008000, // TITLE Do Not Consume if Gained During Cast + SPELL_ATTR2_NO_ACTIVE_PETS = 0x00010000, // TITLE No Active Pets + SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS = 0x00020000, // TITLE Do Not Reset Combat Timers DESCRIPTION Does not reset melee/ranged autoattack timer on cast + SPELL_ATTR2_NO_JUMP_WHILE_CAST_PENDING = 0x00040000, // TITLE No Jump While Cast Pending (client only) + SPELL_ATTR2_ALLOW_WHILE_NOT_SHAPESHIFTED_CASTER_FORM = 0x00080000, // TITLE Allow While Not Shapeshifted (caster form) DESCRIPTION Even if Stances are nonzero, allow spell to be cast outside of shapeshift (though not in a different shapeshift) + SPELL_ATTR2_INITIATE_COMBAT_POST_CAST_ENABLES_AUTO_ATTACK = 0x00100000, // TITLE Initiate Combat Post-Cast (Enables Auto-Attack) + SPELL_ATTR2_FAIL_ON_ALL_TARGETS_IMMUNE = 0x00200000, // TITLE Fail on all targets immune DESCRIPTION Causes BG flags to be dropped if combined with ATTR1_DISPEL_AURAS_ON_IMMUNITY + SPELL_ATTR2_NO_INITIAL_THREAT = 0x00400000, // TITLE No Initial Threat + SPELL_ATTR2_PROC_COOLDOWN_ON_FAILURE = 0x00800000, // TITLE Proc Cooldown On Failure + SPELL_ATTR2_ITEM_CAST_WITH_OWNER_SKILL = 0x01000000, // TITLE Item Cast With Owner Skill + SPELL_ATTR2_DONT_BLOCK_MANA_REGEN = 0x02000000, // TITLE Don't Block Mana Regen + SPELL_ATTR2_NO_SCHOOL_IMMUNITIES = 0x04000000, // TITLE No School Immunities DESCRIPTION Allow aura to be applied despite target being immune to new aura applications + SPELL_ATTR2_IGNORE_WEAPONSKILL = 0x08000000, // TITLE Ignore Weaponskill + SPELL_ATTR2_NOT_AN_ACTION = 0x10000000, // TITLE Not an Action + SPELL_ATTR2_CANT_CRIT = 0x20000000, // TITLE Can't Crit + SPELL_ATTR2_ACTIVE_THREAT = 0x40000000, // TITLE Active Threat + SPELL_ATTR2_RETAIN_ITEM_CAST = 0x80000000 // TITLE Retain Item Cast DESCRIPTION passes m_CastItem to triggered spells }; enum SpellAttr3 diff --git a/src/server/game/Server/Packets/CombatLogPackets.cpp b/src/server/game/Server/Packets/CombatLogPackets.cpp index a420bbcdb1..49a792a4a8 100644 --- a/src/server/game/Server/Packets/CombatLogPackets.cpp +++ b/src/server/game/Server/Packets/CombatLogPackets.cpp @@ -108,6 +108,8 @@ ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::CombatLog::SpellLogMissDe { data << float(debug.HitRoll); data << float(debug.HitRollNeeded); + + return data; } ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::CombatLog::SpellLogMissEntry const& entry) diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 46811a7de8..23c2113dbe 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1939,7 +1939,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo if (SpellShapeshiftFormEntry const* shapeshift = sSpellShapeshiftFormStore.LookupEntry(form)) if (!shapeshift->GetFlags().HasFlag(SpellShapeshiftFormFlags::Stance)) - target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Shapeshifting, GetId()); + target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Shapeshifting, GetSpellInfo()); } else { @@ -3364,7 +3364,7 @@ void AuraEffect::HandleAuraModSchoolImmunity(AuraApplication const* aurApp, uint // remove all flag auras (they are positive, but they must be removed when you are immune) if (GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT) - && GetSpellInfo()->HasAttribute(SPELL_ATTR2_DAMAGE_REDUCED_SHIELD)) + && GetSpellInfo()->HasAttribute(SPELL_ATTR2_FAIL_ON_ALL_TARGETS_IMMUNE)) target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::StealthOrInvis); } @@ -6021,10 +6021,6 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const return; } - // heal for caster damage (must be alive) - if (target != caster && GetSpellInfo()->HasAttribute(SPELL_ATTR2_HEALTH_FUNNEL) && (!caster || !caster->IsAlive())) - return; - // don't regen when permanent aura target has full power if (GetBase()->IsPermanent() && target->IsFullHealth()) return; @@ -6070,23 +6066,6 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const bool haveCastItem = !GetBase()->GetCastItemGUID().IsEmpty(); - // Health Funnel - // damage caster for heal amount - if (target != caster && GetSpellInfo()->HasAttribute(SPELL_ATTR2_HEALTH_FUNNEL)) - { - uint32 funnelDamage = GetSpellInfo()->ManaPerSecond; // damage is not affected by spell power - - if (funnelDamage > healInfo.GetEffectiveHeal() && healInfo.GetEffectiveHeal()) - funnelDamage = healInfo.GetEffectiveHeal(); - - uint32 funnelAbsorb = 0; - Unit::DealDamageMods(caster, funnelDamage, &funnelAbsorb); - caster->SendSpellNonMeleeDamageLog(caster, GetId(), funnelDamage, GetSpellInfo()->GetSchoolMask(), funnelAbsorb, 0, false, 0, false); - - CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - Unit::DealDamage(caster, caster, funnelDamage, &cleanDamage, SELF_DAMAGE, GetSpellInfo()->GetSchoolMask(), GetSpellInfo(), true); - } - // %-based heal - does not proc auras if (GetAuraType() == SPELL_AURA_OBS_MOD_HEALTH) return; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index de7b2ba069..5aed048e6a 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -834,7 +834,7 @@ void Aura::Update(uint32 diff, Unit* caster) { if (m_timeCla > int32(diff)) m_timeCla -= diff; - else if (caster) + else if (caster && (caster == GetOwner() || !GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_TARGET_PER_SECOND_COSTS))) { if (int32 manaPerSecond = m_spellInfo->ManaPerSecond) { @@ -1110,7 +1110,7 @@ bool Aura::IsRemovedOnShapeLost(Unit* target) const { return GetCasterGUID() == target->GetGUID() && m_spellInfo->Stances - && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) + && !m_spellInfo->HasAttribute(SPELL_ATTR2_ALLOW_WHILE_NOT_SHAPESHIFTED_CASTER_FORM) && !m_spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFTED); } @@ -1838,9 +1838,18 @@ bool Aura::IsProcOnCooldown(std::chrono::steady_clock::time_point now) const return m_procCooldown > now; } -void Aura::AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd) +void Aura::AddProcCooldown(SpellProcEntry const* procEntry, std::chrono::steady_clock::time_point now) { - m_procCooldown = cooldownEnd; + // cooldowns should be added to the whole aura (see 51698 area aura) + int32 procCooldown = procEntry->Cooldown.count(); + + /* + if (Unit* caster = GetCaster()) + if (Player* modOwner = caster->GetSpellModOwner()) + modOwner->ApplySpellMod(GetSpellInfo(), SpellModOp::ProcCooldown, procCooldown); + */ + + m_procCooldown = now + Milliseconds(procCooldown); } void Aura::ResetProcCooldown() @@ -1857,15 +1866,35 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); ASSERT(procEntry); + PrepareProcChargeDrop(procEntry, eventInfo); + + // cooldowns should be added to the whole aura (see 51698 area aura) + AddProcCooldown(procEntry, now); +} + + +void Aura::PrepareProcChargeDrop(SpellProcEntry const* procEntry, ProcEventInfo const& eventInfo) +{ // take one charge, aura expiration will be handled in Aura::TriggerProcOnEvent (if needed) if (!(procEntry->AttributesMask & PROC_ATTR_USE_STACKS_FOR_CHARGES)&& IsUsingCharges() && (!eventInfo.GetSpellInfo() || !eventInfo.GetSpellInfo()->HasAttribute(SPELL_ATTR6_DONT_CONSUME_PROC_CHARGES))) { --m_procCharges; SetNeedClientUpdateForTargets(); } +} - // cooldowns should be added to the whole aura (see 51698 area aura) - AddProcCooldown(now + procEntry->Cooldown); +void Aura::ConsumeProcCharges(SpellProcEntry const* procEntry) +{ + // Remove aura if we've used last charge to proc + if (procEntry->AttributesMask & PROC_ATTR_USE_STACKS_FOR_CHARGES) + { + ModStackAmount(-1); + } + else if (IsUsingCharges()) + { + if (!GetCharges()) + Remove(); + } } uint8 Aura::GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const @@ -2033,16 +2062,7 @@ void Aura::TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, Pro CallScriptAfterProcHandlers(aurApp, eventInfo); } - // Remove aura if we've used last charge to proc - if (ASSERT_NOTNULL(sSpellMgr->GetSpellProcEntry(GetId()))->AttributesMask & PROC_ATTR_USE_STACKS_FOR_CHARGES) - { - ModStackAmount(-1); - } - else if (IsUsingCharges()) - { - if (!GetCharges()) - Remove(); - } + ConsumeProcCharges(ASSERT_NOTNULL(sSpellMgr->GetSpellProcEntry(GetSpellInfo()->Id))); } void Aura::_DeleteRemovedApplications() diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 8739e88d48..8369c06c64 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -224,11 +224,13 @@ class TC_GAME_API Aura bool CanStackWith(Aura const* existingAura) const; bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const; - void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd); + void AddProcCooldown(SpellProcEntry const* procEntry, std::chrono::steady_clock::time_point now); void ResetProcCooldown(); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now); + void PrepareProcChargeDrop(SpellProcEntry const* procEntry, ProcEventInfo const& eventInfo); + void ConsumeProcCharges(SpellProcEntry const* procEntry); uint8 GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(uint8 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8caefd757c..1c71217dd5 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -590,7 +590,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO break; default: // Wands - if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTO_REPEAT)) m_attackType = RANGED_ATTACK; else m_attackType = BASE_ATTACK; @@ -624,6 +624,10 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO m_spellState = SPELL_STATE_NULL; _triggeredCastFlags = triggerFlags; + + if (info->HasAttribute(SPELL_ATTR2_DO_NOT_REPORT_SPELL_FAILURE)) + _triggeredCastFlags = TriggerCastFlags(uint32(_triggeredCastFlags) | TRIGGERED_DONT_REPORT_CAST_ERROR); + if (info->HasAttribute(SPELL_ATTR4_CAN_CAST_WHILE_CASTING)) _triggeredCastFlags = TriggerCastFlags(uint32(_triggeredCastFlags) | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); @@ -835,6 +839,41 @@ void Spell::SelectSpellTargets() if (m_targets.HasDst()) AddDestTarget(*m_targets.GetDst(), i); + if (m_spellInfo->Effects[i].TargetA.GetObjectType() == TARGET_OBJECT_TYPE_UNIT + || m_spellInfo->Effects[i].TargetA.GetObjectType() == TARGET_OBJECT_TYPE_UNIT_AND_DEST + || m_spellInfo->Effects[i].TargetB.GetObjectType() == TARGET_OBJECT_TYPE_UNIT + || m_spellInfo->Effects[i].TargetB.GetObjectType() == TARGET_OBJECT_TYPE_UNIT_AND_DEST) + { + if (m_spellInfo->HasAttribute(SPELL_ATTR1_REQUIRE_ALL_TARGETS)) + { + bool noTargetFound = std::none_of(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effectMask = 1u << i](TargetInfo const& target) + { + return target.EffectMask & effectMask; + }); + + if (noTargetFound) + { + SendCastResult(m_spellInfo->Id == 51690 ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_BAD_TARGETS); + finish(false); + return; + } + } + if (m_spellInfo->HasAttribute(SPELL_ATTR2_FAIL_ON_ALL_TARGETS_IMMUNE)) + { + bool anyNonImmuneTargetFound = std::any_of(m_UniqueTargetInfo.begin(), m_UniqueTargetInfo.end(), [effectMask = 1u << i](TargetInfo const& target) + { + return target.EffectMask & effectMask && target.MissCondition != SPELL_MISS_IMMUNE && target.MissCondition != SPELL_MISS_IMMUNE2; + }); + + if (!anyNonImmuneTargetFound) + { + SendCastResult(SPELL_FAILED_IMMUNE); + finish(false); + return; + } + } + } + if (m_spellInfo->IsChanneled()) { // maybe do this for all spells? @@ -2008,8 +2047,9 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (isBouncingFar) searchRadius *= chainTargets; + WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list tempTargets; - SearchAreaTargets(tempTargets, searchRadius, target, m_caster, objectType, selectType, condList); + SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList); tempTargets.remove(target); // remove targets which are always invalid for chain spells @@ -2037,7 +2077,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if (deficit > maxHPDeficit && target->IsWithinDist(unit, jumpRadius) && target->IsWithinLOSInMap(unit, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && chainSource->IsWithinDist(unit, jumpRadius) && chainSource->IsWithinLOSInMap(unit, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) { foundItr = itr; maxHPDeficit = deficit; @@ -2052,19 +2092,22 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || target->IsWithinDist(*itr, jumpRadius)) && target->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if ((!isBouncingFar || chainSource->IsWithinDist(*itr, jumpRadius)) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } - else if (target->GetDistanceOrder(*itr, *foundItr) && target->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } } // not found any valid target - chain ends if (foundItr == tempTargets.end()) break; - target = *foundItr; + + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER)) + chainSource = *foundItr; + + targets.push_back(*foundItr); tempTargets.erase(foundItr); - targets.push_back(target); --chainTargets; } } @@ -2099,7 +2142,7 @@ void Spell::prepareDataForTriggerSystem() break; case SPELL_DAMAGE_CLASS_RANGED: // Auto attack - if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) + if (m_spellInfo->HasAttribute(SPELL_ATTR2_AUTO_REPEAT)) { m_procAttacker = PROC_FLAG_DEAL_RANGED_ATTACK; m_procVictim = PROC_FLAG_TAKE_RANGED_ATTACK; @@ -2113,7 +2156,7 @@ void Spell::prepareDataForTriggerSystem() default: if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && m_spellInfo->EquippedItemSubClassMask & (1 << ITEM_SUBCLASS_WEAPON_WAND) - && m_spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG)) // Wands auto attack + && m_spellInfo->HasAttribute(SPELL_ATTR2_AUTO_REPEAT)) // Wands auto attack { m_procAttacker = PROC_FLAG_DEAL_RANGED_ATTACK; m_procVictim = PROC_FLAG_TAKE_RANGED_ATTACK; @@ -3087,7 +3130,9 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) if (*i < 0) unit->RemoveAurasDueToSpell(-(*i)); else - unit->CastSpell(unit, *i, m_caster->GetGUID()); + unit->CastSpell(unit, *i, CastSpellExtraArgs(TRIGGERED_FULL_MASK) + .SetOriginalCaster(m_caster->GetGUID()) + .SetTriggeringSpell(this)); } } } @@ -3320,8 +3365,8 @@ SpellCastResult Spell::prepare(SpellCastTargets const& targets, AuraEffect const { // stealth must be removed at cast starting (at show channel bar) // skip triggered spell (item equip spell casting and other not explicit character casts/item uses) - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && m_spellInfo->IsBreakingStealth() && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) - unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action); + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_AN_ACTION)) + unitCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Action, m_spellInfo); unitCaster->SetCurrentCastSpell(this); } @@ -3628,7 +3673,8 @@ void Spell::_cast(bool skipCheck) unitCaster->RemoveAurasDueToSpell(-id); } else - m_caster->CastSpell(m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster, id, true); + m_caster->CastSpell(m_targets.GetUnitTarget() ? m_targets.GetUnitTarget() : m_caster, id, CastSpellExtraArgs(TRIGGERED_FULL_MASK) + .SetTriggeringSpell(this)); } } @@ -3672,8 +3718,8 @@ void Spell::_cast(bool skipCheck) if (!(hitMask & PROC_HIT_CRITICAL)) hitMask |= PROC_HIT_NORMAL; - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && !m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_ACTION_AURA_INTERRUPT_FLAGS)) - m_originalCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::ActionDelayed); + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_AURA_INTERRUPT_FLAGS) && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_AN_ACTION)) + m_originalCaster->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::ActionDelayed, m_spellInfo); if (!m_spellInfo->HasAttribute(SPELL_ATTR3_SUPPRESS_CASTER_PROCS)) Unit::ProcSkillsAndAuras(m_originalCaster, nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr); @@ -4112,7 +4158,7 @@ void Spell::finish(bool ok) } } - if (!found && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) + if (!found && !m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS)) { unitCaster->resetAttackTimer(BASE_ATTACK); if (unitCaster->haveOffhandWeapon()) @@ -4306,9 +4352,6 @@ void Spell::SendCastResult(SpellCastResult result, uint32* param1 /*= nullptr*/, if (result == SPELL_CAST_OK) return; - if (m_spellInfo->HasAttribute(SPELL_ATTR2_DONT_REPORT_SPELL_FAILURE)) - return; - if (m_caster->GetTypeId() != TYPEID_PLAYER) return; @@ -5542,7 +5585,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_spellInfo->HasAttribute(SPELL_ATTR0_USES_RANGED_SLOT) || m_spellInfo->IsNextMeleeSwingSpell() || m_spellInfo->HasAttribute(SPELL_ATTR1_INITIATES_COMBAT_ENABLES_AUTO_ATTACK) - || m_spellInfo->HasAttribute(SPELL_ATTR2_INITIATE_COMBAT_POST_CAST) + || m_spellInfo->HasAttribute(SPELL_ATTR2_INITIATE_COMBAT_POST_CAST_ENABLES_AUTO_ATTACK) || m_spellInfo->HasEffect(SPELL_EFFECT_ATTACK) || m_spellInfo->HasEffect(SPELL_EFFECT_NORMALIZED_WEAPON_DMG) || m_spellInfo->HasEffect(SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL) @@ -5753,7 +5796,7 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (DynamicObject* dynObj = m_caster->ToUnit()->GetDynObject(m_triggeredByAuraSpell->Id)) losTarget = dynObj; - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !target->IsWithinLOSInMap(losTarget, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) return SPELL_FAILED_LINE_OF_SIGHT; } } @@ -5765,13 +5808,17 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint float x, y, z; m_targets.GetDstPos()->GetPosition(x, y, z); - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) && !m_spellInfo->HasAttribute(SPELL_ATTR5_SKIP_CHECKCAST_LOS_CHECK) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS) && !m_caster->IsWithinLOS(x, y, z, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) return SPELL_FAILED_LINE_OF_SIGHT; } // check pet presence if (Unit* unitCaster = m_caster->ToUnit()) { + if (m_spellInfo->HasAttribute(SPELL_ATTR2_NO_ACTIVE_PETS)) + if (!unitCaster->GetPetGUID().IsEmpty()) + return SPELL_FAILED_ALREADY_HAVE_PET; + for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (m_spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET) @@ -6181,7 +6228,9 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) if (Pet* pet = unitCaster->ToPlayer()->GetPet()) - pet->CastSpell(pet, 32752, pet->GetGUID()); + pet->CastSpell(pet, 32752, CastSpellExtraArgs(TRIGGERED_FULL_MASK) + .SetOriginalCaster(pet->GetGUID()) + .SetTriggeringSpell(this)); } else if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET_FIRST)) return SPELL_FAILED_ALREADY_HAVE_SUMMON; @@ -6435,6 +6484,9 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (m_CastItem) return SPELL_FAILED_ITEM_ENCHANT_TRADE_WINDOW; + if (m_spellInfo->HasAttribute(SPELL_ATTR2_ENCHANT_OWN_ITEM_ONLY)) + return SPELL_FAILED_ITEM_ENCHANT_TRADE_WINDOW; + if (m_caster->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_NOT_TRADING; @@ -7148,7 +7200,8 @@ SpellCastResult Spell::CheckItems(uint32* param1 /*= nullptr*/, uint32* param2 / return SPELL_FAILED_DONT_REPORT; } else - player->CastSpell(player, m_spellInfo->Effects[EFFECT_1].CalcValue(), false); // move this to anywhere + player->CastSpell(player, m_spellInfo->Effects[EFFECT_1].CalcValue(), CastSpellExtraArgs() + .SetTriggeringSpell(this)); // move this to anywhere return SPELL_FAILED_DONT_REPORT; } } @@ -7607,7 +7660,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo } // check for ignore LOS on the effect itself - if (m_spellInfo->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS)) + if (m_spellInfo->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, nullptr, SPELL_DISABLE_LOS)) return true; // check if gameobject ignores LOS @@ -7616,7 +7669,7 @@ bool Spell::CheckEffectTarget(Unit const* target, uint32 eff, Position const* lo return true; // if spell is triggered, need to check for LOS disable on the aura triggering it and inherit that behaviour - if (IsTriggered() && m_triggeredByAuraSpell && (m_triggeredByAuraSpell->HasAttribute(SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS) || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_triggeredByAuraSpell->Id, nullptr, SPELL_DISABLE_LOS))) + if (IsTriggered() && m_triggeredByAuraSpell && (m_triggeredByAuraSpell->HasAttribute(SPELL_ATTR2_IGNORE_LINE_OF_SIGHT) || DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_triggeredByAuraSpell->Id, nullptr, SPELL_DISABLE_LOS))) return true; /// @todo shit below shouldn't be here, but it's temporary @@ -7969,7 +8022,7 @@ void Spell::DoEffectOnLaunchTarget(TargetInfo& targetInfo, float multiplier, uin if (m_spellInfo->IsPositive() && !m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)) return false; - return (m_spellInfo->CausesInitialThreat() || m_spellInfo->HasAttribute(SPELL_ATTR2_INITIATE_COMBAT_POST_CAST) || unit->IsEngaged()); + return (m_spellInfo->CausesInitialThreat() || unit->IsEngaged()); }(); if (triggerCombat) diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 1fad5bb3cc..02846a45f6 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -25,6 +25,7 @@ class Item; class AuraEffect; +class Spell; enum class SpellInterruptFlags : uint32 { @@ -198,6 +199,7 @@ struct TC_GAME_API CastSpellExtraArgs CastSpellExtraArgs& SetTriggerFlags(TriggerCastFlags flag) { TriggerFlags = flag; return *this; } CastSpellExtraArgs& SetCastItem(Item* item) { CastItem = item; return *this; } + CastSpellExtraArgs& SetTriggeringSpell(Spell const* triggeringSpell) { TriggeringSpell = triggeringSpell; return *this; } CastSpellExtraArgs& SetTriggeringAura(AuraEffect const* triggeringAura) { TriggeringAura = triggeringAura; return *this; } CastSpellExtraArgs& SetOriginalCaster(ObjectGuid const& guid) { OriginalCaster = guid; return *this; } CastSpellExtraArgs& AddSpellMod(SpellValueMod mod, int32 val) { SpellValueOverrides.AddMod(mod, val); return *this; } @@ -205,6 +207,7 @@ struct TC_GAME_API CastSpellExtraArgs TriggerCastFlags TriggerFlags = TRIGGERED_NONE; Item* CastItem = nullptr; + Spell const* TriggeringSpell = nullptr; AuraEffect const* TriggeringAura = nullptr; ObjectGuid OriginalCaster = ObjectGuid::Empty; struct @@ -220,6 +223,12 @@ struct TC_GAME_API CastSpellExtraArgs std::vector> data; } SpellValueOverrides; + + CastSpellExtraArgs(CastSpellExtraArgs const&) = delete; + CastSpellExtraArgs(CastSpellExtraArgs&&) = delete; + + CastSpellExtraArgs& operator=(CastSpellExtraArgs const&) = delete; + CastSpellExtraArgs& operator=(CastSpellExtraArgs&&) = delete; }; enum class SummonPropertiesParamType : uint8 diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 01b8eed76a..927ae1730c 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -567,14 +567,21 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex) } } - CastSpellExtraArgs args(m_originalCasterGUID); + // original caster guid only for GO cast + CastSpellExtraArgs args(TRIGGERED_FULL_MASK); + args.SetOriginalCaster(m_originalCasterGUID); + + if (!m_castItemGUID.IsEmpty() && sSpellMgr->AssertSpellInfo(m_spellInfo->Effects[effIndex].TriggerSpell)->HasAttribute(SPELL_ATTR2_RETAIN_ITEM_CAST)) + if (Unit const* unitAuraCaster = args.TriggeringAura->GetCaster()) + if (Player const* triggeringAuraCaster = unitAuraCaster->ToPlayer()) + args.CastItem = triggeringAuraCaster->GetItemByGuid(m_castItemGUID); + // set basepoints for trigger with value effect if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE) for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), damage); - // original caster guid only for GO cast - m_caster->CastSpell(targets, spellInfo->Id, args); + m_caster->CastSpell(std::move(targets), spellInfo->Id, args); } void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index c117d2053e..41d416e9cb 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1406,7 +1406,7 @@ bool SpellInfo::IsRequiringDeadTarget() const bool SpellInfo::IsAllowingDeadTarget() const { - if (HasAttribute(SPELL_ATTR2_CAN_TARGET_DEAD) || Targets & (TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_DEAD)) + if (HasAttribute(SPELL_ATTR2_ALLOW_DEAD_TARGET) || Targets & (TARGET_FLAG_CORPSE_ALLY | TARGET_FLAG_CORPSE_ENEMY | TARGET_FLAG_UNIT_DEAD)) return true; for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) @@ -1478,11 +1478,6 @@ bool SpellInfo::IsNextMeleeSwingSpell() const return HasAttribute(SpellAttr0(SPELL_ATTR0_ON_NEXT_SWING_NO_DAMAGE | SPELL_ATTR0_ON_NEXT_SWING)); } -bool SpellInfo::IsBreakingStealth() const -{ - return !HasAttribute(SPELL_ATTR1_ALLOW_WHILE_STEALTHED); -} - bool SpellInfo::IsRangedWeaponSpell() const { return (EquippedItemClass == ITEM_CLASS_WEAPON) && (EquippedItemSubClassMask & ITEM_SUBCLASS_MASK_WEAPON_RANGED); @@ -1490,7 +1485,7 @@ bool SpellInfo::IsRangedWeaponSpell() const bool SpellInfo::IsAutoRepeatRangedSpell() const { - return HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG); + return HasAttribute(SPELL_ATTR2_AUTO_REPEAT); } bool SpellInfo::CausesInitialThreat() const @@ -1568,7 +1563,7 @@ bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const return true; // these spells (Cyclone for example) can pierce all... - if (HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if (HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { // ...but not these (Divine shield, Ice block, Cyclone and Banish for example) if (auraSpellInfo->Mechanic != MECHANIC_IMMUNE_SHIELD && @@ -1596,7 +1591,7 @@ bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const // These auras (Cyclone for example) are not dispelable if ((auraSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && auraSpellInfo->Mechanic != MECHANIC_NONE) - || auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + || auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) return false; return true; @@ -1710,7 +1705,7 @@ SpellCastResult SpellInfo::CheckShapeshift(uint32 form) const else { // needs shapeshift - if (!HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) && Stances != 0) + if (!HasAttribute(SPELL_ATTR2_ALLOW_WHILE_NOT_SHAPESHIFTED_CASTER_FORM) && Stances != 0) return SPELL_FAILED_ONLY_SHAPESHIFT; } @@ -1906,7 +1901,7 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co if (caster->GetTypeId() == TYPEID_PLAYER) { // Do not allow these spells to target creatures not tapped by us (Banish, Polymorph, many quest spells) - if (HasAttribute(SPELL_ATTR2_CANT_TARGET_TAPPED)) + if (HasAttribute(SPELL_ATTR2_CANNOT_CAST_ON_TAPPED)) if (Creature const* targetCreature = unitTarget->ToCreature()) if (targetCreature->hasLootRecipient() && !targetCreature->isTappedBy(caster->ToPlayer())) return SPELL_FAILED_CANT_CAST_ON_TAPPED; @@ -3176,7 +3171,7 @@ bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInf { ImmunityInfo const* immuneInfo = _immunityInfo + i; - if (!auraSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if (!auraSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask) if ((auraSpellInfo->SchoolMask & schoolImmunity) != 0) @@ -3223,7 +3218,7 @@ bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInf if (auraImmuneItr != immuneInfo->AuraTypeImmune.cend()) isImmuneToAuraEffectApply = true; - if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(effIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(effIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { if (uint32 applyHarmfulAuraImmunityMask = immuneInfo->ApplyHarmfulAuraImmuneMask) if ((auraSpellInfo->GetSchoolMask() & applyHarmfulAuraImmunityMask) != 0) @@ -3269,7 +3264,7 @@ bool SpellInfo::SpellCancelsAuraEffect(SpellInfo const* auraSpellInfo, uint8 aur break; case SPELL_AURA_SCHOOL_IMMUNITY: case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: - if (auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE) || !(auraSpellInfo->SchoolMask & miscValue)) + if (auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) || !(auraSpellInfo->SchoolMask & miscValue)) continue; break; case SPELL_AURA_DISPEL_IMMUNITY: @@ -3654,7 +3649,7 @@ SpellInfo const* SpellInfo::GetAuraRankForLevel(uint8 level) const return this; // Client ignores spell with these attributes (sub_53D9D0) - if (HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF) || HasAttribute(SPELL_ATTR2_UNK3)) + if (HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF) || HasAttribute(SPELL_ATTR2_ALLOW_LOW_LEVEL_BUFF)) return this; bool needRankSelection = false; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 01ba479967..9ba6a09a56 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3365,7 +3365,7 @@ void SpellMgr::LoadSpellInfoCorrections() // Tremor Totem (instant pulse) ApplySpellFix({ 8145 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; spellInfo->AttributesEx5 |= SPELL_ATTR5_START_PERIODIC_AT_APPLY; }); @@ -4210,7 +4210,7 @@ void SpellMgr::LoadSpellInfoCorrections() ApplySpellFix({ 75509 }, [](SpellInfo* spellInfo) { spellInfo->AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE; - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Combustion and Consumption Heroic versions lacks radius data @@ -4247,7 +4247,7 @@ void SpellMgr::LoadSpellInfoCorrections() 57143 // Life Burst (Wyrmrest Skytalon) }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Arcane Barrage (cast by players and NONMELEEDAMAGELOG with caster Scion of Eternity (original caster)). @@ -4429,7 +4429,7 @@ void SpellMgr::LoadSpellInfoCorrections() // Frozen Orb ApplySpellFix({ 92267 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Cho'Gall @@ -4529,7 +4529,7 @@ void SpellMgr::LoadSpellInfoCorrections() }, [](SpellInfo* spellInfo) { spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_DEST_DEST); - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Fiery Blaze @@ -4630,7 +4630,7 @@ void SpellMgr::LoadSpellInfoCorrections() // Ride Vehicle ApplySpellFix({ 43671 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Devouring Flames @@ -5237,7 +5237,7 @@ void SpellMgr::LoadSpellInfoCorrections() 93261 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Lightning Strike (Force Cast) @@ -5249,7 +5249,7 @@ void SpellMgr::LoadSpellInfoCorrections() // Lightning Strike (Visual) ApplySpellFix({ 88230 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Lightning Strike (Damage) @@ -5371,7 +5371,7 @@ void SpellMgr::LoadSpellInfoCorrections() // Static Shock ApplySpellFix({ 87873 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // ENDOF THRONE OF THE FOUR WINDS SPELLS @@ -5516,7 +5516,7 @@ void SpellMgr::LoadSpellInfoCorrections() 94472 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // @@ -5526,7 +5526,7 @@ void SpellMgr::LoadSpellInfoCorrections() // Eject Passenger ApplySpellFix({ 78643 }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + spellInfo->AttributesEx2 |= SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; }); // Ignition diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index d5193f50f9..0e5836c225 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -4872,7 +4872,8 @@ class spell_item_blind_spot : public SpellScript if (AuraEffect const* effect = target->GetAuraEffect(spellId, EFFECT_0, target->GetGUID())) { std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - effect->GetBase()->AddProcCooldown(now + 30s); + if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(spellId)) + effect->GetBase()->AddProcCooldown(procEntry, now + 30s); } }