aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Spells
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2022-04-28 18:31:26 +0200
committerShauren <shauren.trinity@gmail.com>2022-04-28 18:31:26 +0200
commitdda375b9868d6dbe2a4d58b386bb90ae41d25e0d (patch)
tree9a4a55dd37d787b1384ed55ba99be0cc92cfa2e2 /src/server/game/Spells
parentc88b602a2c7eda598a4205dd0ec9f562c31f21b0 (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.cpp8
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp57
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h4
-rw-r--r--src/server/game/Spells/Spell.cpp87
-rw-r--r--src/server/game/Spells/SpellDefines.h7
-rw-r--r--src/server/game/Spells/SpellEffects.cpp26
-rw-r--r--src/server/game/Spells/SpellInfo.cpp27
-rw-r--r--src/server/game/Spells/SpellInfo.h1
-rw-r--r--src/server/game/Spells/SpellMgr.cpp6
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)