diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/server/game/DataStores/DBCEnums.h | 26 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 10 | ||||
| -rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 21 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.cpp | 51 | ||||
| -rw-r--r-- | src/server/game/Spells/Spell.h | 30 | ||||
| -rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 3 |
6 files changed, 85 insertions, 56 deletions
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 95afccc8d13..b367da13854 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -1862,12 +1862,28 @@ enum SpellCategoryFlags enum class SpellEffectAttributes { None = 0, - UnaffectedByInvulnerability = 0x000001, // not cancelled by immunities - NoScaleWithStack = 0x000040, + NoImmunity = 0x000001, /*NYI*/ // not cancelled by immunities + PositionIsFacingRelative = 0x000002, /*NYI*/ + JumpChargeUnitMeleeRange = 0x000004, /*NYI*/ + JumpChargeUnitStrictPathCheck = 0x000008, /*NYI*/ + ExcludeOwnParty = 0x000010, /*NYI*/ + AlwaysAoeLineOfSight = 0x000020, + SuppressPointsStacking = 0x000040, ChainFromInitialTarget = 0x000080, - StackAuraAmountOnRecast = 0x008000, // refreshing periodic auras with this attribute will add remaining damage to new aura - AllowAnyExplicitTarget = 0x100000, - IgnoreDuringCooldownTimeRateCalculation = 0x800000 + UncontrolledNoBackwards = 0x000100, /*NYI*/ + AuraPointsStack = 0x000200, /*NYI*/ // refreshing periodic auras with this attribute will add remaining damage to new aura + NoCopyDamageInterruptsOrProcs = 0x000400, /*NYI*/ + AddTargetCombatReachToAOE = 0x000800, /*NYI*/ + IsHarmful = 0x001000, + ForceScaleToOverrideCameraMinHeight = 0x002000, /*NYI*/ + PlayersOnly = 0x004000, + ComputePointsOnlyAtCastTime = 0x008000, /*NYI*/ + EnforceLineOfSightToChainTargets = 0x010000, + AreaEffectsUseTargetRadius = 0x020000, /*NYI*/ + TeleportWithVehicle = 0x040000, /*NYI*/ + ScalePointsByChallengeModeDamageScaler = 0x080000, /*NYI*/ + DontFailSpellOnTargetingFailure = 0x100000, /*NYI*/ + IgnoreDuringCooldownTimeRateCalculation = 0x800000, /*NYI*/ }; DEFINE_ENUM_FLAG(SpellEffectAttributes); diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 9ad9cc23074..3efbbbf0696 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -714,7 +714,7 @@ int32 AuraEffect::CalculateAmount(Unit* caster) } GetBase()->CallScriptEffectCalcAmountHandlers(this, amount, m_canBeRecalculated); - if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::NoScaleWithStack)) + if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::SuppressPointsStacking)) amount *= GetBase()->GetStackAmount(); _estimatedAmount = CalculateEstimatedAmount(caster, amount); @@ -725,7 +725,7 @@ int32 AuraEffect::CalculateAmount(Unit* caster) Optional<float> AuraEffect::CalculateEstimatedAmount(Unit const* caster, Unit* target, SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, int32 amount, uint8 stack, AuraEffect const* aurEff) { - uint32 stackAmountForBonuses = !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::NoScaleWithStack) ? stack : 1; + uint32 stackAmountForBonuses = !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::SuppressPointsStacking) ? stack : 1; switch (spellEffectInfo.ApplyAuraName) { @@ -5391,7 +5391,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL); - uint32 stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::NoScaleWithStack) ? GetBase()->GetStackAmount() : 1; + uint32 stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::SuppressPointsStacking) ? GetBase()->GetStackAmount() : 1; // ignore negative values (can be result apply spellmods to aura damage uint32 damage = std::max(GetAmount(), 0); @@ -5525,7 +5525,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c CleanDamage cleanDamage = CleanDamage(0, 0, GetSpellInfo()->GetAttackType(), MELEE_HIT_NORMAL); - uint32 stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::NoScaleWithStack) ? GetBase()->GetStackAmount() : 1; + uint32 stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::SuppressPointsStacking) ? GetBase()->GetStackAmount() : 1; // ignore negative values (can be result apply spellmods to aura damage uint32 damage = std::max(GetAmount(), 0); @@ -5651,7 +5651,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const if (GetBase()->IsPermanent() && target->IsFullHealth()) return; - uint32 stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::NoScaleWithStack) ? GetBase()->GetStackAmount() : 1; + uint32 stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::SuppressPointsStacking) ? GetBase()->GetStackAmount() : 1; // ignore negative values (can be result apply spellmods to aura damage uint32 damage = std::max(GetAmount(), 0); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index cb504c77865..b8095cab43c 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -25,6 +25,7 @@ #include "Log.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" +#include "PhasingHandler.h" #include "Player.h" #include "ScriptMgr.h" #include "Spell.h" @@ -2506,7 +2507,7 @@ void UnitAura::FillTargetMap(std::unordered_map<Unit*, uint32>& targets, Unit* c if (GetUnitOwner()->HasUnitState(UNIT_STATE_ISOLATED)) continue; - std::vector<Unit*> units; + std::vector<WorldObject*> units; ConditionContainer* condList = spellEffectInfo.ImplicitTargetConditions; float radius = spellEffectInfo.CalcRadius(ref); @@ -2562,16 +2563,20 @@ void UnitAura::FillTargetMap(std::unordered_map<Unit*, uint32>& targets, Unit* c if (selectionType != TARGET_CHECK_DEFAULT) { - Trinity::WorldObjectSpellAreaTargetCheck check(radius, GetUnitOwner(), ref, GetUnitOwner(), m_spellInfo, selectionType, condList, TARGET_OBJECT_TYPE_UNIT); - Trinity::UnitListSearcher<Trinity::WorldObjectSpellAreaTargetCheck> searcher(GetUnitOwner(), units, check); - Cell::VisitAllObjects(GetUnitOwner(), searcher, radius + extraSearchRadius); + if (uint32 containerTypeMask = Spell::GetSearcherTypeMask(m_spellInfo, spellEffectInfo, TARGET_OBJECT_TYPE_UNIT, condList)) + { + Trinity::WorldObjectSpellAreaTargetCheck check(radius, GetUnitOwner(), ref, GetUnitOwner(), m_spellInfo, selectionType, condList, TARGET_OBJECT_TYPE_UNIT); + Trinity::WorldObjectListSearcher searcher(GetUnitOwner(), units, check, containerTypeMask); + searcher.i_phaseShift = &PhasingHandler::GetAlwaysVisiblePhaseShift(); + Spell::SearchTargets(searcher, containerTypeMask, GetUnitOwner(), GetUnitOwner(), radius + extraSearchRadius); - // by design WorldObjectSpellAreaTargetCheck allows not-in-world units (for spells) but for auras it is not acceptable - units.erase(std::remove_if(units.begin(), units.end(), [this](Unit* unit) { return !unit->IsSelfOrInSameMap(GetUnitOwner()); }), units.end()); + // by design WorldObjectSpellAreaTargetCheck allows not-in-world units (for spells) but for auras it is not acceptable + Trinity::Containers::EraseIf(units, [this](WorldObject const* unit) { return !unit->IsSelfOrInSameMap(GetUnitOwner()); }); + } } - for (Unit* unit : units) - targets[unit] |= 1 << spellEffectInfo.EffectIndex; + for (WorldObject* unit : units) + targets[static_cast<Unit*>(unit)] |= 1 << spellEffectInfo.EffectIndex; } } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 4ba4c975c6e..6a5d1eb855e 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -920,6 +920,7 @@ void Spell::SelectEffectImplicitTargets(SpellEffectInfo const& spellEffectInfo, spellEffectInfo.ImplicitTargetConditions == effects[j].ImplicitTargetConditions && spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex::TargetA) == effects[j].CalcRadius(m_caster, SpellTargetIndex::TargetA) && spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex::TargetB) == effects[j].CalcRadius(m_caster, SpellTargetIndex::TargetB) && + spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::PlayersOnly) == effects[j].EffectAttributes.HasFlag(SpellEffectAttributes::PlayersOnly) && CheckScriptEffectImplicitTargets(spellEffectInfo.EffectIndex, j)) { effectMask |= 1 << j; @@ -1176,7 +1177,7 @@ void Spell::SelectImplicitNearbyTargets(SpellEffectInfo const& spellEffectInfo, } } - WorldObject* target = SearchNearbyTarget(range, targetType.GetObjectType(), targetType.GetCheckType(), condList); + WorldObject* target = SearchNearbyTarget(spellEffectInfo, range, targetType.GetObjectType(), targetType.GetCheckType(), condList); float randomRadius = 0.0f; switch (targetType.GetTarget()) { @@ -1298,7 +1299,7 @@ void Spell::SelectImplicitConeTargets(SpellEffectInfo const& spellEffectInfo, Sp ConditionContainer* condList = spellEffectInfo.ImplicitTargetConditions; float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue->RadiusMod; - if (uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList)) + if (uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList)) { float extraSearchRadius = radius > 0.0f ? EXTRA_CELL_SEARCH_RADIUS : 0.0f; Trinity::WorldObjectSpellConeTargetCheck check(coneSrc, DegToRad(coneAngle), m_spellInfo->Width ? m_spellInfo->Width : m_caster->GetCombatReach(), radius, m_caster, m_spellInfo, selectionType, condList, objectType); @@ -1399,15 +1400,15 @@ void Spell::SelectImplicitAreaTargets(SpellEffectInfo const& spellEffectInfo, Sp if (!m_caster->IsUnit() || !m_caster->ToUnit()->IsInRaidWith(targetedUnit)) targets.push_back(m_targets.GetUnitTarget()); else - SearchAreaTargets(targets, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); + SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); } break; case TARGET_UNIT_CASTER_AND_SUMMONS: targets.push_back(m_caster); - SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); + SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); break; default: - SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); + SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); break; } @@ -1958,7 +1959,7 @@ void Spell::SelectImplicitLineTargets(SpellEffectInfo const& spellEffectInfo, Sp ConditionContainer* condList = spellEffectInfo.ImplicitTargetConditions; float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue->RadiusMod; - if (uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList)) + if (uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList)) { Trinity::WorldObjectSpellLineTargetCheck check(m_caster, dst, m_spellInfo->Width ? m_spellInfo->Width : m_caster->GetCombatReach(), radius, m_caster, m_spellInfo, selectionType, condList, objectType); Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellLineTargetCheck> searcher(m_caster, targets, check, containerTypeMask); @@ -2091,7 +2092,7 @@ void Spell::SelectEffectTypeImplicitTargets(SpellEffectInfo const& spellEffectIn } } -uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionContainer* condList) +uint32 Spell::GetSearcherTypeMask(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, SpellTargetObjectTypes objType, ConditionContainer const* condList) { // this function selects which containers need to be searched for spell target uint32 retMask = GRID_MAP_TYPE_MASK_ALL; @@ -2116,11 +2117,11 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionConta break; } - if (m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER)) + if (spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER) || spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::PlayersOnly)) retMask &= GRID_MAP_TYPE_MASK_CORPSE | GRID_MAP_TYPE_MASK_PLAYER; - if (m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_GHOSTS)) + if (spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_GHOSTS)) retMask &= GRID_MAP_TYPE_MASK_PLAYER; - if (m_spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) + if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) retMask &= ~GRID_MAP_TYPE_MASK_PLAYER; if (condList) @@ -2157,10 +2158,10 @@ void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* } } -WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +WorldObject* Spell::SearchNearbyTarget(SpellEffectInfo const& spellEffectInfo, float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) { WorldObject* target = nullptr; - uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); + uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList); if (!containerTypeMask) return nullptr; @@ -2171,9 +2172,9 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec return target; } -void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList) +void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList) { - uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList); + uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList); if (!containerTypeMask) return; @@ -2226,7 +2227,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list<WorldObject*> tempTargets; - SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions); + SearchAreaTargets(tempTargets, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions); tempTargets.remove(target); // remove targets which are always invalid for chain spells @@ -2265,13 +2266,17 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar { for (std::list<WorldObject*>::iterator itr = tempTargets.begin(); itr != tempTargets.end(); ++itr) { - if (foundItr == tempTargets.end()) - { - if (chainSource->IsWithinDist(*itr, jumpRadius) && IsWithinLOS(chainSource, *itr, false, VMAP::ModelIgnoreFlags::M2)) - foundItr = itr; - } - else if (chainSource->GetDistanceOrder(*itr, *foundItr) && IsWithinLOS(chainSource, *itr, false, VMAP::ModelIgnoreFlags::M2)) - foundItr = itr; + bool isBestDistanceMatch = foundItr != tempTargets.end() ? chainSource->GetDistanceOrder(*itr, *foundItr) : chainSource->IsWithinDist(*itr, jumpRadius); + if (!isBestDistanceMatch) + continue; + + if (!IsWithinLOS(chainSource, *itr, false, VMAP::ModelIgnoreFlags::M2)) + continue; + + if (spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::EnforceLineOfSightToChainTargets) && !IsWithinLOS(m_caster, *itr, false, VMAP::ModelIgnoreFlags::M2)) + continue; + + foundItr = itr; } } // not found any valid target - chain ends @@ -7991,7 +7996,7 @@ bool Spell::CheckEffectTarget(Unit const* target, SpellEffectInfo const& spellEf } default: { - if (!losPosition || m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT)) + if (!losPosition || m_spellInfo->HasAttribute(SPELL_ATTR5_ALWAYS_AOE_LINE_OF_SIGHT) || spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes::AlwaysAoeLineOfSight)) { // Get GO cast coordinates if original caster -> GO WorldObject* caster = nullptr; diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 3b3c37f4a80..9c5f969af72 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -445,11 +445,11 @@ class TC_GAME_API Spell void SelectEffectTypeImplicitTargets(SpellEffectInfo const& spellEffectInfo); - uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionContainer* condList); - template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius); + static uint32 GetSearcherTypeMask(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, SpellTargetObjectTypes objType, ConditionContainer const* condList); + template<class SEARCHER> static void SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius); - WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList = nullptr); - void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList); + WorldObject* SearchNearbyTarget(SpellEffectInfo const& spellEffectInfo, float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList = nullptr); + void SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList); void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal); GameObject* SearchSpellFocus(); @@ -915,20 +915,20 @@ namespace Trinity { struct TC_GAME_API WorldObjectSpellTargetCheck { - protected: - WorldObject* _caster; - WorldObject* _referer; - SpellInfo const* _spellInfo; - SpellTargetCheckTypes _targetSelectionType; - std::unique_ptr<ConditionSourceInfo> _condSrcInfo; - ConditionContainer const* _condList; + protected: + WorldObject* _caster; + WorldObject* _referer; + SpellInfo const* _spellInfo; + SpellTargetCheckTypes _targetSelectionType; + std::unique_ptr<ConditionSourceInfo> _condSrcInfo; + ConditionContainer const* _condList; SpellTargetObjectTypes _objectType; - WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, - SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); - ~WorldObjectSpellTargetCheck(); + WorldObjectSpellTargetCheck(WorldObject* caster, WorldObject* referer, SpellInfo const* spellInfo, + SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType); + ~WorldObjectSpellTargetCheck(); - bool operator()(WorldObject* target) const; + bool operator()(WorldObject* target) const; }; struct TC_GAME_API WorldObjectSpellNearbyTargetCheck : public WorldObjectSpellTargetCheck diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 7020e63b246..1fd65ec5199 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -4544,6 +4544,9 @@ bool _isPositiveEffectImpl(SpellInfo const* spellInfo, SpellEffectInfo const& ef if (spellInfo->HasAttribute(SPELL_ATTR4_AURA_IS_BUFF)) return true; + if (effect.EffectAttributes.HasFlag(SpellEffectAttributes::IsHarmful)) + return false; + visited.insert({ spellInfo, effect.EffectIndex }); //We need scaling level info for some auras that compute bp 0 or positive but should be debuffs |
