diff options
| author | Shauren <shauren.trinity@gmail.com> | 2022-04-28 18:31:26 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2022-04-28 18:31:26 +0200 |
| commit | dda375b9868d6dbe2a4d58b386bb90ae41d25e0d (patch) | |
| tree | 9a4a55dd37d787b1384ed55ba99be0cc92cfa2e2 /src/server/game/Spells | |
| parent | c88b602a2c7eda598a4205dd0ec9f562c31f21b0 (diff) | |
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
Diffstat (limited to 'src/server/game/Spells')
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 8 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 57 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 4 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 87 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellDefines.h | 7 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 26 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 27 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.h | 1 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellMgr.cpp | 6 |
9 files changed, 145 insertions, 78 deletions
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 5c550058033..326e33e1808 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -1774,7 +1774,7 @@ void AuraEffect::HandleAuraModShapeshift(AuraApplication const* aurApp, uint8 mo } if (!shapeInfo->GetFlags().HasFlag(SpellShapeshiftFormFlags::Stance)) - target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Shapeshifting, GetId()); + target->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Shapeshifting, GetSpellInfo()); } else { @@ -3218,7 +3218,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); } @@ -5499,10 +5499,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; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 2b8353dd6d8..2e14479eb97 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -809,7 +809,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 (!m_periodicCosts.empty()) { @@ -1114,7 +1114,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); } @@ -1711,9 +1711,15 @@ bool Aura::IsProcOnCooldown(TimePoint now) const return m_procCooldown > now; } -void Aura::AddProcCooldown(TimePoint cooldownEnd) +void Aura::AddProcCooldown(SpellProcEntry const* procEntry, TimePoint 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() @@ -1730,22 +1736,36 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetSpellInfo()); ASSERT(procEntry); + PrepareProcChargeDrop(procEntry, eventInfo); + + // cooldowns should be added to the whole aura (see 51698 area aura) + AddProcCooldown(procEntry, now); + + SetLastProcSuccessTime(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_DO_NOT_CONSUME_RESOURCES))) { --m_procCharges; SetNeedClientUpdateForTargets(); } +} - // 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); - - AddProcCooldown(now + Milliseconds(procCooldown)); - - SetLastProcSuccessTime(now); +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(); + } } uint32 Aura::GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now) const @@ -1913,16 +1933,7 @@ void Aura::TriggerProcOnEvent(uint32 procEffectMask, AuraApplication* aurApp, Pr CallScriptAfterProcHandlers(aurApp, eventInfo); } - // Remove aura if we've used last charge to proc - if (ASSERT_NOTNULL(sSpellMgr->GetSpellProcEntry(m_spellInfo))->AttributesMask & PROC_ATTR_USE_STACKS_FOR_CHARGES) - { - ModStackAmount(-1); - } - else if (IsUsingCharges()) - { - if (!GetCharges()) - Remove(); - } + ConsumeProcCharges(ASSERT_NOTNULL(sSpellMgr->GetSpellProcEntry(GetSpellInfo()))); } float Aura::CalcPPMProcChance(Unit* actor) const diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 752712accd7..3aa899513ba 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -247,11 +247,13 @@ class TC_GAME_API Aura bool CanStackWith(Aura const* existingAura) const; bool IsProcOnCooldown(TimePoint now) const; - void AddProcCooldown(TimePoint cooldownEnd); + void AddProcCooldown(SpellProcEntry const* procEntry, TimePoint now); void ResetProcCooldown(); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now); + void PrepareProcChargeDrop(SpellProcEntry const* procEntry, ProcEventInfo const& eventInfo); + void ConsumeProcCharges(SpellProcEntry const* procEntry); uint32 GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, TimePoint now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(uint32 procEffectMask, AuraApplication* aurApp, ProcEventInfo& eventInfo); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index d42b2ce8812..80e1fb4e547 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -540,6 +540,10 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) 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); @@ -754,6 +758,41 @@ void Spell::SelectSpellTargets() if (m_targets.HasDst()) AddDestTarget(*m_targets.GetDst(), spellEffectInfo.EffectIndex); + if (spellEffectInfo.TargetA.GetObjectType() == TARGET_OBJECT_TYPE_UNIT + || spellEffectInfo.TargetA.GetObjectType() == TARGET_OBJECT_TYPE_UNIT_AND_DEST + || spellEffectInfo.TargetB.GetObjectType() == TARGET_OBJECT_TYPE_UNIT + || spellEffectInfo.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 << spellEffectInfo.EffectIndex](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 << spellEffectInfo.EffectIndex](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? @@ -2048,8 +2087,9 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar if (isBouncingFar) searchRadius *= chainTargets; + WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list<WorldObject*> 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 @@ -2077,7 +2117,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar if (Unit* unit = (*itr)->ToUnit()) { uint32 deficit = unit->GetMaxHealth() - unit->GetHealth(); - if ((deficit > maxHPDeficit || foundItr == tempTargets.end()) && 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; @@ -2092,19 +2132,22 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& 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; } } @@ -2139,7 +2182,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; @@ -2153,7 +2196,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; @@ -3328,8 +3371,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); // Do not register as current spell when requested to ignore cast in progress // We don't want to interrupt that other spell with cast time @@ -3707,8 +3750,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); Unit::ProcSkillsAndAuras(m_originalCaster, nullptr, procAttacker, PROC_FLAG_NONE, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_CAST, hitMask, this, nullptr, nullptr); @@ -4137,7 +4180,7 @@ void Spell::finish(bool ok) if (IsAutoActionResetSpell()) { - if (!m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_RESET_AUTO_ACTIONS)) + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_DO_NOT_RESET_COMBAT_TIMERS)) { unitCaster->resetAttackTimer(BASE_ATTACK); if (unitCaster->haveOffhandWeapon()) @@ -5356,7 +5399,7 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 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_UNK20) + || 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) @@ -5570,7 +5613,7 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 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_ALWAYS_AOE_LINE_OF_SIGHT) && !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_ALWAYS_AOE_LINE_OF_SIGHT) && !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; } } @@ -5582,13 +5625,17 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 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_ALWAYS_AOE_LINE_OF_SIGHT) && !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_ALWAYS_AOE_LINE_OF_SIGHT) && !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 (SpellEffectInfo const& spellEffectInfo : m_spellInfo->GetEffects()) { if (spellEffectInfo.TargetA.GetTarget() == TARGET_UNIT_PET) @@ -6458,6 +6505,9 @@ SpellCastResult Spell::CheckCast(bool strict, int32* param1 /*= nullptr*/, int32 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; @@ -7730,7 +7780,7 @@ bool Spell::CheckEffectTarget(Unit const* target, SpellEffectInfo const& spellEf } // 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 @@ -7739,7 +7789,7 @@ bool Spell::CheckEffectTarget(Unit const* target, SpellEffectInfo const& spellEf 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 @@ -8925,6 +8975,7 @@ CastSpellTargetArg::CastSpellTargetArg(WorldObject* target) CastSpellExtraArgs& CastSpellExtraArgs::SetTriggeringSpell(Spell const* triggeringSpell) { + TriggeringSpell = triggeringSpell; if (triggeringSpell) { OriginalCastItemLevel = triggeringSpell->m_castItemLevel; diff --git a/src/server/game/Spells/SpellDefines.h b/src/server/game/Spells/SpellDefines.h index 56b40fcb439..c975c761d06 100644 --- a/src/server/game/Spells/SpellDefines.h +++ b/src/server/game/Spells/SpellDefines.h @@ -454,6 +454,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; Difficulty CastDifficulty = Difficulty(0); @@ -472,6 +473,12 @@ struct TC_GAME_API CastSpellExtraArgs std::vector<std::pair<SpellValueMod, int32>> data; } SpellValueOverrides; + + CastSpellExtraArgs(CastSpellExtraArgs const&) = delete; + CastSpellExtraArgs(CastSpellExtraArgs&&) = delete; + + CastSpellExtraArgs& operator=(CastSpellExtraArgs const&) = delete; + CastSpellExtraArgs& operator=(CastSpellExtraArgs&&) = delete; }; struct SpellCastVisual diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 4a9aaa12ef9..38e0661c9d9 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -641,18 +641,24 @@ void Spell::EffectTriggerSpell() if (effectInfo->Effect == SPELL_EFFECT_TRIGGER_SPELL) delay = Milliseconds(effectInfo->MiscValue); - CastSpellExtraArgs args(TRIGGERED_FULL_MASK); - args.SetOriginalCaster(m_originalCasterGUID); - args.SetTriggeringSpell(this); - // set basepoints for trigger with value effect - if (effectInfo->Effect == SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE) - for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) - args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), damage); - - m_caster->m_Events.AddEventAtOffset([caster = m_caster, targets, triggered_spell_id, args]() mutable + m_caster->m_Events.AddEventAtOffset([caster = m_caster, targets, originalCaster = m_originalCasterGUID, castItemGuid = m_castItemGUID, originalCastId = m_castId, + spellEffectInfo = effectInfo, value = damage, itemLevel = m_castItemLevel]() mutable { // original caster guid only for GO cast - caster->CastSpell(std::move(targets), triggered_spell_id, args); + CastSpellExtraArgs args(TRIGGERED_FULL_MASK); + args.SetOriginalCaster(originalCaster); + args.OriginalCastId = originalCastId; + args.OriginalCastItemLevel = itemLevel; + if (!castItemGuid.IsEmpty() && sSpellMgr->AssertSpellInfo(spellEffectInfo->TriggerSpell, caster->GetMap()->GetDifficultyID())->HasAttribute(SPELL_ATTR2_RETAIN_ITEM_CAST)) + if (Player const* triggeringAuraCaster = Object::ToPlayer(args.TriggeringAura->GetCaster())) + args.CastItem = triggeringAuraCaster->GetItemByGuid(castItemGuid); + + // set basepoints for trigger with value effect + if (spellEffectInfo->Effect == SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE) + for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i) + args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), value); + + caster->CastSpell(std::move(targets), spellEffectInfo->TriggerSpell, args); }, delay); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 035a19f744e..45a23d29435 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1593,7 +1593,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 (SpellEffectInfo const& effect : GetEffects()) @@ -1661,11 +1661,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 (SpellFamilyName == SPELLFAMILY_HUNTER && !(SpellFamilyFlags[1] & 0x10000000)) // for 53352, cannot find better way @@ -1675,12 +1670,12 @@ bool SpellInfo::IsRangedWeaponSpell() const bool SpellInfo::IsAutoRepeatRangedSpell() const { - return HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG); + return HasAttribute(SPELL_ATTR2_AUTO_REPEAT); } bool SpellInfo::HasInitialAggro() const { - return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)); + return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR2_NO_INITIAL_THREAT)); } bool SpellInfo::HasHitDelay() const @@ -1784,7 +1779,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 && @@ -1812,7 +1807,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; @@ -1929,7 +1924,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; } @@ -2138,7 +2133,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; @@ -3600,7 +3595,7 @@ bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInf ImmunityInfo const& immuneInfo = effectInfo.GetImmunityInfo(); - 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) @@ -3646,7 +3641,7 @@ bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInf if (auraImmuneItr != immuneInfo.AuraTypeImmune.cend()) isImmuneToAuraEffectApply = true; - if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(auraSpellEffectInfo.EffectIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE)) + if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(auraSpellEffectInfo.EffectIndex) && !auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { if (uint32 applyHarmfulAuraImmunityMask = immuneInfo.ApplyHarmfulAuraImmuneMask) if ((auraSpellInfo->GetSchoolMask() & applyHarmfulAuraImmunityMask) != 0) @@ -3692,7 +3687,7 @@ bool SpellInfo::SpellCancelsAuraEffect(AuraEffect const* aurEff) const break; case SPELL_AURA_SCHOOL_IMMUNITY: case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL: - if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR2_UNAFFECTED_BY_AURA_SCHOOL_IMMUNE) || !(aurEff->GetSpellInfo()->SchoolMask & miscValue)) + if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) || !(aurEff->GetSpellInfo()->SchoolMask & miscValue)) continue; break; case SPELL_AURA_DISPEL_IMMUNITY: @@ -4268,7 +4263,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) || HasAttribute(SPELL_ATTR3_DRAIN_SOUL)) + if (HasAttribute(SPELL_ATTR0_AURA_IS_DEBUFF) || HasAttribute(SPELL_ATTR2_ALLOW_LOW_LEVEL_BUFF) || HasAttribute(SPELL_ATTR3_DRAIN_SOUL)) return this; bool needRankSelection = false; diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 0000291e369..793fd8b5a0d 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -515,7 +515,6 @@ class TC_GAME_API SpellInfo bool IsMoveAllowedChannel() const; bool NeedsComboPoints() const; bool IsNextMeleeSwingSpell() const; - bool IsBreakingStealth() const; bool IsRangedWeaponSpell() const; bool IsAutoRepeatRangedSpell() const; bool HasInitialAggro() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 07dc8bb7cde..63c75f92f19 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -4418,7 +4418,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; }); // Awaken Flames @@ -4442,7 +4442,7 @@ void SpellMgr::LoadSpellInfoCorrections() { // All spells work even without these changes. The LOS attribute is due to problem // from collision between maps & gos with active destroyed state. - 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)). @@ -4581,7 +4581,7 @@ void SpellMgr::LoadSpellInfoCorrections() ApplySpellFix({ 42525 }, [](SpellInfo* spellInfo) { spellInfo->AttributesEx3 |= SPELL_ATTR3_DEATH_PERSISTENT; - spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_DEAD; + spellInfo->AttributesEx2 |= SPELL_ATTR2_ALLOW_DEAD_TARGET; }); // Soul Sickness (Forge of Souls) |
