diff --git a/src/server/game/Combat/ThreatManager.cpp b/src/server/game/Combat/ThreatManager.cpp index 3ba72590dc..585102ed77 100644 --- a/src/server/game/Combat/ThreatManager.cpp +++ b/src/server/game/Combat/ThreatManager.cpp @@ -657,7 +657,7 @@ void ThreatManager::ProcessAIUpdates() void ThreatManager::ForwardThreatForAssistingMe(Unit* assistant, float baseAmount, SpellInfo const* spell, bool ignoreModifiers) { - if (spell && spell->HasAttribute(SPELL_ATTR1_NO_THREAT)) // shortcut, none of the calls would do anything + if (spell && (spell->HasAttribute(SPELL_ATTR1_NO_THREAT) || spell->HasAttribute(SPELL_ATTR4_NO_HELPFUL_THREAT))) // shortcut, none of the calls would do anything return; if (_threatenedByMe.empty()) return; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 1a262241f9..276d210a4e 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2150,29 +2150,6 @@ void Creature::LoadTemplateImmunities() } } -bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const -{ - if (!spellInfo) - return false; - - bool immunedToAllEffects = true; - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - if (!spellInfo->Effects[i].IsEffect()) - continue; - - if (!IsImmunedToSpellEffect(spellInfo, i, caster)) - { - immunedToAllEffects = false; - break; - } - } - if (immunedToAllEffects) - return true; - - return Unit::IsImmunedToSpell(spellInfo, caster); -} - bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const { if (!spellInfo->Effects[index].IsEffect()) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 1ae8a5e1ab..08c40b716c 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -129,7 +129,6 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool IsClassTrainerOf(Player const* player) const; bool CanCreatureAttack(Unit const* victim, bool force = true) const; void LoadTemplateImmunities(); - bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const override; bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; bool isElite() const; bool isWorldBoss() const; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 77dc279f4e..6a300f666d 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2658,14 +2658,6 @@ SpellMissInfo WorldObject::MagicSpellHitResult(Unit* victim, SpellInfo const* sp // Chance resist mechanic (select max value from every mechanic spell effect) int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; - // Chance resist debuff - if (!spellInfo->IsPositive() && !spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) - { - // resistance chance for binary spells, equals to average damage reduction of non-binary spell - if (spellInfo->HasAttribute(SPELL_ATTR0_CU_BINARY_SPELL) && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_MAGIC)) - resist_chance += int32(Unit::CalculateAverageResistReduction(this, spellInfo->GetSchoolMask(), victim, spellInfo) * 10000.f); // 100 for spell calculations, and 100 for return value percentage - } - // Roll chance if (resist_chance > 0 && rand < (tmp += resist_chance)) return SPELL_MISS_RESIST; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 54141678ac..6a36378bd2 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1162,7 +1162,7 @@ void Pet::_LoadAuras(uint32 timediff) } // negative effects should continue counting down after logout - if (remaintime != -1 && (!spellInfo->IsPositive() || spellInfo->HasAttribute(SPELL_ATTR4_FADES_WHILE_LOGGED_OUT))) + if (remaintime != -1 && (!spellInfo->IsPositive() || spellInfo->HasAttribute(SPELL_ATTR4_AURA_EXPIRES_OFFLINE))) { if (remaintime/IN_MILLISECONDS <= int32(timediff)) continue; @@ -1373,7 +1373,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel } // Store pet scaling auras in a own vector to handle the updating more efficient - if (spellInfo->HasAttribute(SPELL_ATTR4_IS_PET_SCALING)) + if (spellInfo->HasAttribute(SPELL_ATTR4_OWNER_POWER_SCALING)) { m_petScalingAuras.push_back(spellInfo->Id); return true; @@ -1382,7 +1382,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel m_spells[spellId] = newspell; // Do not go further if this Attribute exists - if (spellInfo->HasAttribute(SPELL_ATTR4_HIDDEN_IN_SPELLBOOK) && !IsWarlockMinion()) + if (spellInfo->HasAttribute(SPELL_ATTR4_NOT_IN_SPELLBOOK) && !IsWarlockMinion()) return true; if (spellInfo->IsPassive() && (!spellInfo->CasterAuraState || HasAuraState(AuraStateType(spellInfo->CasterAuraState)))) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7effb9f21d..f8934192fc 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18027,7 +18027,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) } // negative effects should continue counting down after logout - if (remaintime != -1 && ((!spellInfo->IsPositive() && spellInfo->Id != 15007) || spellInfo->HasAttribute(SPELL_ATTR4_FADES_WHILE_LOGGED_OUT))) // Resurrection sickness should not fade while logged out + if (remaintime != -1 && ((!spellInfo->IsPositive() && spellInfo->Id != 15007) || spellInfo->HasAttribute(SPELL_ATTR4_AURA_EXPIRES_OFFLINE))) // Resurrection sickness should not fade while logged out { if (remaintime/IN_MILLISECONDS <= int32(timediff)) continue; @@ -21293,7 +21293,7 @@ void Player::PetSpellInitialize() continue; // Do not send this spells, they are used indirectly - if (sSpellMgr->GetSpellInfo(itr->first)->HasAttribute(SPELL_ATTR4_HIDDEN_IN_SPELLBOOK)) + if (sSpellMgr->GetSpellInfo(itr->first)->HasAttribute(SPELL_ATTR4_NOT_IN_SPELLBOOK)) continue; data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first, itr->second.active)); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 2a52120660..607eb94e56 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -785,7 +785,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons // interrupting auras with SpellAuraInterruptFlags::Damage before checking !damage (absorbed damage breaks that type of auras) if (spellProto) { - if (!spellProto->HasAttribute(SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS)) + if (!spellProto->HasAttribute(SPELL_ATTR4_REACTIVE_DAMAGE_PROC)) victim->RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::Damage, spellProto); } else @@ -930,7 +930,9 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons // Part of Evade mechanics. DoT's and Thorns / Retribution Aura do not contribute to this if (damagetype != DOT && damage > 0 && !victim->GetOwnerGUID().IsPlayer() && (!spellProto || !spellProto->HasAura(SPELL_AURA_DAMAGE_SHIELD))) victim->ToCreature()->SetLastDamagedTime(GameTime::GetGameTime() + MAX_AGGRO_RESET_TIME); - victim->GetThreatManager().AddThreat(attacker, float(damage), spellProto); + + if (attacker && (!spellProto || !spellProto->HasAttribute(SPELL_ATTR4_NO_HARMFUL_THREAT))) + victim->GetThreatManager().AddThreat(attacker, float(damage), spellProto); } else // victim is a player { @@ -1041,8 +1043,8 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage* damageInfo, int32 dama SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask); - // Spells with SPELL_ATTR4_FIXED_DAMAGE ignore resilience because their damage is based off another spell's damage. - if (!spellInfo->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + // Spells with SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS ignore resilience because their damage is based off another spell's damage. + if (!spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS)) { if (Unit::IsDamageReducedByArmor(damageSchoolMask, spellInfo)) damage = Unit::CalcArmorReducedDamage(this, victim, damage, spellInfo, attackType); @@ -1581,17 +1583,6 @@ static float GetArmorReduction(float armor, uint8 attackerLevel) if ((schoolMask & SPELL_SCHOOL_MASK_HOLY) && victim->GetTypeId() != TYPEID_UNIT) return 0; - // Ignore spells that can't be resisted - if (spellInfo && spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) - return 0; - - // Ignore spells that can't be resisted - if (spellInfo) - { - if (spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_RESISTANCES)) - return 0; - } - float const averageResist = Unit::CalculateAverageResistReduction(attacker, schoolMask, victim, spellInfo); float discreteResistProbability[11] = { }; @@ -4002,7 +3993,7 @@ void Unit::RemoveArenaAuras() RemoveAppliedAuras([](AuraApplication const* aurApp) { Aura const* aura = aurApp->GetBase(); - return (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_DONT_REMOVE_IN_ARENA) // don't remove stances, shadowform, pally/hunter auras + return (!aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_ALLOW_ENTERING_ARENA) // don't remove stances, shadowform, pally/hunter auras && !aura->IsPassive() // don't remove passive auras && (aurApp->IsPositive() || !aura->GetSpellInfo()->HasAttribute(SPELL_ATTR3_ALLOW_AURA_WHILE_DEAD))) || // not negative death persistent auras aura->GetSpellInfo()->HasAttribute(SPELL_ATTR5_REMOVE_ON_ARENA_ENTER); // special marker, always remove @@ -6583,8 +6574,8 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui } } - // Spells with SPELL_ATTR4_FIXED_DAMAGE should only benefit from mechanic damage mod auras. - if (!spellProto->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + // Spells with SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS should only benefit from mechanic damage mod auras. + if (!spellProto->HasAttribute(SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS)) { // from positive and negative SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN // multiplicative bonus, for example Dispersion + Shadowform (0.10*0.85=0.085) @@ -7377,6 +7368,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste immuneToAllEffects = false; break; } + if (spellInfo->HasAttribute(SPELL_ATTR4_NO_PARTIAL_IMMUNITY)) + return true; } if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects. diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index c2ba77f4ef..61e264ea8b 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1616,7 +1616,7 @@ class TC_GAME_API Unit : public WorldObject float CalculateDefaultCoefficient(SpellInfo const* spellInfo, DamageEffectType damagetype) const; void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply); - virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const; // redefined in Creature + bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const; uint32 GetSchoolImmunityMask() const; uint32 GetDamageImmunityMask() const; uint32 GetMechanicImmunityMask() const; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index b44dd5e393..1672c2f7eb 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -496,40 +496,40 @@ enum SpellAttr3 : uint32 SPELL_ATTR3_NOT_ON_AOE_IMMUNE = 0x80000000 /*NYI, no aoe immunity implementation*/ // TITLE Not On AOE Immune }; -enum SpellAttr4 +enum SpellAttr4 : uint32 { - SPELL_ATTR4_IGNORE_RESISTANCES = 0x00000001, // 0 spells with this attribute will completely ignore the target's resistance (these spells can't be resisted) - SPELL_ATTR4_PROC_ONLY_ON_CASTER = 0x00000002, // 1 proc only on effects with TARGET_UNIT_CASTER? - SPELL_ATTR4_FADES_WHILE_LOGGED_OUT = 0x00000004, // 2 duration is removed from aura while player is logged out - SPELL_ATTR4_UNK3 = 0x00000008, // 3 - SPELL_ATTR4_UNK4 = 0x00000010, // 4 This will no longer cause guards to attack on use?? - SPELL_ATTR4_UNK5 = 0x00000020, // 5 - SPELL_ATTR4_NOT_STEALABLE = 0x00000040, // 6 although such auras might be dispellable, they cannot be stolen - SPELL_ATTR4_CAN_CAST_WHILE_CASTING = 0x00000080, // 7 Can be cast while another cast is in progress - see CanCastWhileCasting(SpellRec const*,CGUnit_C *,int &) - SPELL_ATTR4_FIXED_DAMAGE = 0x00000100, // 8 Ignores resilience and any (except mechanic related) damage or % damage taken auras on target. - SPELL_ATTR4_TRIGGER_ACTIVATE = 0x00000200, // 9 initially disabled / trigger activate from event (Execute, Riposte, Deep Freeze end other) - SPELL_ATTR4_SPELL_VS_EXTEND_COST = 0x00000400, // 10 Rogue Shiv have this flag - SPELL_ATTR4_UNK11 = 0x00000800, // 11 - SPELL_ATTR4_AURA_IS_BUFF = 0x00001000, // 12 Aura Is Buff - SPELL_ATTR4_UNK13 = 0x00002000, // 13 - SPELL_ATTR4_DAMAGE_DOESNT_BREAK_AURAS = 0x00004000, // 14 doesn't break auras by damage from these spells - SPELL_ATTR4_HIDDEN_IN_SPELLBOOK = 0x00008000, // 15 - SPELL_ATTR4_NOT_USABLE_IN_ARENA_OR_RATED_BG = 0x00010000, // 16 Cannot be used in both Arenas or Rated Battlegrounds - SPELL_ATTR4_USABLE_IN_ARENA = 0x00020000, // 17 - SPELL_ATTR4_AREA_TARGET_CHAIN = 0x00040000, // 18 (NYI)hits area targets one after another instead of all at once - SPELL_ATTR4_UNK19 = 0x00080000, // 19 proc dalayed, after damage or don't proc on absorb? - SPELL_ATTR4_NOT_CHECK_SELFCAST_POWER = 0x00100000, // 20 supersedes message "More powerful spell applied" for self casts. - SPELL_ATTR4_DONT_REMOVE_IN_ARENA = 0x00200000, // 21 Pally aura, dk presence, dudu form, warrior stance, shadowform, hunter track - SPELL_ATTR4_UNK22 = 0x00400000, // 22 Seal of Command (42058, 57770) and Gymer's Smash 55426 - SPELL_ATTR4_CANT_TRIGGER_ITEM_SPELLS = 0x00800000, // 23 spells with this flag should not trigger item spells / enchants (mostly in conjunction with SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) - SPELL_ATTR4_UNK24 = 0x01000000, // 24 some shoot spell - SPELL_ATTR4_IS_PET_SCALING = 0x02000000, // 25 pet scaling auras - SPELL_ATTR4_CAST_ONLY_IN_OUTLAND = 0x04000000, // 26 Can only be used in Outland. - SPELL_ATTR4_UNK27 = 0x08000000, // 27 - SPELL_ATTR4_UNK28 = 0x10000000, // 28 Aimed Shot - SPELL_ATTR4_UNK29 = 0x20000000, // 29 - SPELL_ATTR4_UNK30 = 0x40000000, // 30 - SPELL_ATTR4_UNK31 = 0x80000000 // 31 Polymorph (chicken) 228 and Sonic Boom (38052, 38488) + SPELL_ATTR4_NO_CAST_LOG = 0x00000001, // TITLE No Cast Log + SPELL_ATTR4_CLASS_TRIGGER_ONLY_ON_TARGET = 0x00000002, // TITLE Class Trigger Only On Target + SPELL_ATTR4_AURA_EXPIRES_OFFLINE = 0x00000004, // TITLE Aura Expires Offline DESCRIPTION Debuffs (except Resurrection Sickness) will automatically do this + SPELL_ATTR4_NO_HELPFUL_THREAT = 0x00000008, // TITLE No Helpful Threat + SPELL_ATTR4_NO_HARMFUL_THREAT = 0x00000010, // TITLE No Harmful Threat + SPELL_ATTR4_ALLOW_CLIENT_TARGETING = 0x00000020, // TITLE Allow Client Targeting DESCRIPTION Allows client to send spell targets for this spell. Applies only to pet spells, without this attribute CMSG_PET_ACTION is sent instead of CMSG_PET_CAST_SPELL + SPELL_ATTR4_CANNOT_BE_STOLEN = 0x00000040, // TITLE Cannot Be Stolen + SPELL_ATTR4_ALLOW_CAST_WHILE_CASTING = 0x00000080, // TITLE Allow Cast While Casting DESCRIPTION Ignores already in-progress cast and still casts + SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS = 0x00000100, // TITLE Ignore Damage Taken Modifiers + SPELL_ATTR4_COMBAT_FEEDBACK_WHEN_USABLE = 0x00000200, // TITLE Combat Feedback When Usable (client only) + SPELL_ATTR4_WEAPON_SPEED_COST_SCALING = 0x00000400, // TITLE Weapon Speed Cost Scaling DESCRIPTION Adds 10 to power cost for each 1s of weapon speed + SPELL_ATTR4_NO_PARTIAL_IMMUNITY = 0x00000800, // TITLE No Partial Immunity + SPELL_ATTR4_AURA_IS_BUFF = 0x00001000, // TITLE Aura Is Buff + SPELL_ATTR4_DO_NOT_LOG_CASTER = 0x00002000, // TITLE Do Not Log Caster + SPELL_ATTR4_REACTIVE_DAMAGE_PROC = 0x00004000, // TITLE Reactive Damage Proc DESCRIPTION Damage from spells with this attribute doesn't break auras that normally break on damage taken + SPELL_ATTR4_NOT_IN_SPELLBOOK = 0x00008000, // TITLE Not In Spellbook + SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND = 0x00010000, // TITLE Not In Arena or Rated Battleground DESCRIPTION Makes spell unusable despite CD <= 10min + SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS = 0x00020000, // TITLE Ignore Default Arena Restrictions DESCRIPTION Makes spell usable despite CD > 10min + SPELL_ATTR4_BOUNCY_CHAIN_MISSILES = 0x00040000, // TITLE Bouncy Chain Missiles DESCRIPTION Hits area targets over time instead of all at once + SPELL_ATTR4_ALLOW_PROC_WHILE_SITTING = 0x00080000, // TITLE Allow Proc While Sitting + SPELL_ATTR4_AURA_NEVER_BOUNCES = 0x00100000, // TITLE Aura Never Bounces + SPELL_ATTR4_ALLOW_ENTERING_ARENA = 0x00200000, // TITLE Allow Entering Arena + SPELL_ATTR4_PROC_SUPPRESS_SWING_ANIM = 0x00400000, // TITLE Proc Suppress Swing Anim + SPELL_ATTR4_SUPPRESS_WEAPON_PROCS = 0x00800000, // TITLE Suppress Weapon Procs + SPELL_ATTR4_AUTO_RANGED_COMBAT = 0x01000000, // TITLE Auto Ranged Combat + SPELL_ATTR4_OWNER_POWER_SCALING = 0x02000000, // TITLE Owner Power Scaling + SPELL_ATTR4_ONLY_FLYING_AREAS = 0x04000000, // TITLE Only Flying Areas + SPELL_ATTR4_FORCE_DISPLAY_CASTBAR = 0x08000000, // TITLE Force Display Castbar + SPELL_ATTR4_IGNORE_COMBAT_TIMER = 0x10000000, // TITLE Ignore Combat Timer + SPELL_ATTR4_AURA_BOUNCE_FAILS_SPELL = 0x20000000, // TITLE Aura Bounce Fails Spell + SPELL_ATTR4_OBSOLETE = 0x40000000, // TITLE Obsolete + SPELL_ATTR4_USE_FACING_FROM_SPELL = 0x80000000 // TITLE Use Facing From Spell }; enum SpellAttr5 @@ -546,7 +546,7 @@ enum SpellAttr5 SPELL_ATTR5_START_PERIODIC_AT_APPLY = 0x00000200, // 9 begin periodic tick at aura apply SPELL_ATTR5_HIDE_DURATION = 0x00000400, // 10 do not send duration to client SPELL_ATTR5_ALLOW_TARGET_OF_TARGET_AS_TARGET = 0x00000800, // 11 (NYI) uses target's target as target if original target not valid (intervene for example) - SPELL_ATTR5_UNK12 = 0x00001000, // 12 Cleave related? + SPELL_ATTR5_MELEE_CHAIN_TARGETING = 0x00001000, // 12 Cleave related? SPELL_ATTR5_SPELL_HASTE_AFFECTS_PERIODIC = 0x00002000, // 13 Spell Haste Affects Periodic SPELL_ATTR5_NOT_USABLE_WHILE_CHARMED = 0x00004000, // 14 Charmed units cannot cast this spell SPELL_ATTR5_TREAT_AS_AREA_EFFECT = 0x00008000, // 15 Treat As Area Effect diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 546c2f7751..c23fdbe3d5 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -5818,14 +5818,14 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const damage = damageReducedArmor; } - if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS)) { if (GetSpellInfo()->Effects[GetEffIndex()].IsTargetingArea() || GetSpellInfo()->Effects[GetEffIndex()].IsAreaAuraEffect() || GetSpellInfo()->Effects[GetEffIndex()].Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) damage = target->CalculateAOEAvoidance(damage, m_spellInfo->SchoolMask, GetBase()->GetCasterGUID()); } int32 dmg = damage; - if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE) && GetBase()->CanApplyResilience()) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS) && caster && caster->CanApplyResilience()) Unit::ApplyResilience(target, &dmg); damage = dmg; @@ -5908,14 +5908,14 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c damage = damageReducedArmor; } - if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE)) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS)) { if (GetSpellInfo()->Effects[GetEffIndex()].IsTargetingArea() || GetSpellInfo()->Effects[GetEffIndex()].IsAreaAuraEffect() || GetSpellInfo()->Effects[GetEffIndex()].Effect == SPELL_EFFECT_PERSISTENT_AREA_AURA) damage = target->CalculateAOEAvoidance(damage, m_spellInfo->SchoolMask, GetBase()->GetCasterGUID()); } int32 dmg = damage; - if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_FIXED_DAMAGE) && GetBase()->CanApplyResilience()) + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS) && caster && caster->CanApplyResilience()) Unit::ApplyResilience(target, &dmg); damage = dmg; @@ -6106,7 +6106,8 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con if (gainAmount) { gainedAmount = caster->ModifyPower(powerType, gainAmount); - target->GetThreatManager().AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo(), true); + if (!GetSpellInfo()->HasAttribute(SPELL_ATTR4_NO_HELPFUL_THREAT)) + target->GetThreatManager().AddThreat(caster, float(gainedAmount) * 0.5f, GetSpellInfo(), true); } // Drain Mana - Mana Feed effect diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index a533038a53..baea918c5c 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -2027,6 +2027,10 @@ uint8 Aura::GetProcEffectMask(AuraApplication* aurApp, ProcEventInfo& eventInfo, if (target->GetGUID() != GetCasterGUID()) return 0; + if (!m_spellInfo->HasAttribute(SPELL_ATTR4_ALLOW_PROC_WHILE_SITTING)) + if (!target->IsStandState()) + return 0; + if (roll_chance_f(CalcProcChance(*procEntry, eventInfo))) return procEffectMask; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 7951126d9b..95021b1264 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -628,7 +628,7 @@ m_caster((info->HasAttribute(SPELL_ATTR6_CAST_BY_CHARMER) && caster->GetCharmerO 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)) + if (info->HasAttribute(SPELL_ATTR4_ALLOW_CAST_WHILE_CASTING)) _triggeredCastFlags = TriggerCastFlags(uint32(_triggeredCastFlags) | TRIGGERED_IGNORE_CAST_IN_PROGRESS | TRIGGERED_CAST_DIRECTLY); m_CastItem = nullptr; @@ -1723,14 +1723,22 @@ void Spell::SelectImplicitChainTargets(SpellEffIndex effIndex, SpellImplicitTarg std::list targets; SearchChainTargets(targets, maxTargets - 1, target, targetType.GetObjectType(), targetType.GetCheckType() - , m_spellInfo->Effects[effIndex].ImplicitTargetConditions, targetType.GetTarget() == TARGET_UNIT_TARGET_CHAINHEAL_ALLY); + , m_spellInfo->Effects[effIndex], targetType.GetTarget() == TARGET_UNIT_TARGET_CHAINHEAL_ALLY); // Chain primary target is added earlier CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType); + Position const* losPosition = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; + for (std::list::iterator itr = targets.begin(); itr != targets.end(); ++itr) + { if (Unit* unit = (*itr)->ToUnit()) - AddUnitTarget(unit, effMask, false); + AddUnitTarget(unit, effMask, false, true, losPosition); + + if (!m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER)) + losPosition = *itr; + } + } } @@ -2012,7 +2020,7 @@ void Spell::SearchAreaTargets(std::list& targets, float range, Pos SearchTargets > (searcher, containerTypeMask, m_caster, position, range); } -void Spell::SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionContainer* condList, bool isChainHeal) +void Spell::SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal) { // max dist for jump target selection float jumpRadius = 0.0f; @@ -2037,31 +2045,28 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar break; } - // chain lightning/heal spells and similar - allow to jump at larger distance and go out of los - bool isBouncingFar = (m_spellInfo->HasAttribute(SPELL_ATTR4_AREA_TARGET_CHAIN) - || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE - || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC); - // max dist which spell can reach - float searchRadius = jumpRadius; - if (isBouncingFar) - searchRadius *= chainTargets; + float searchRadius = [&]() + { + if (m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER)) + return GetMinMaxRange(false).second; + + return jumpRadius * chainTargets; + }(); WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target; std::list tempTargets; - SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList); + SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions); tempTargets.remove(target); // remove targets which are always invalid for chain spells // for some spells allow only chain targets in front of caster (swipe for example) - if (!isBouncingFar) + if (m_spellInfo->HasAttribute(SPELL_ATTR5_MELEE_CHAIN_TARGETING)) { - for (std::list::iterator itr = tempTargets.begin(); itr != tempTargets.end();) + tempTargets.remove_if([&](WorldObject* object) { - std::list::iterator checkItr = itr++; - if (!m_caster->HasInArc(static_cast(M_PI), *checkItr)) - tempTargets.erase(checkItr); - } + return !m_caster->HasInArc(static_cast(M_PI), object); + }); } while (chainTargets) @@ -2092,7 +2097,7 @@ void Spell::SearchChainTargets(std::list& targets, uint32 chainTar { if (foundItr == tempTargets.end()) { - if ((!isBouncingFar || chainSource->IsWithinDist(*itr, jumpRadius)) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) + if (chainSource->IsWithinDist(*itr, jumpRadius) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) foundItr = itr; } else if (chainSource->GetDistanceOrder(*itr, *foundItr) && chainSource->IsWithinLOSInMap(*itr, LINEOFSIGHT_ALL_CHECKS, VMAP::ModelIgnoreFlags::M2)) @@ -2275,23 +2280,38 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= // Spell have speed - need calculate incoming time // Incoming time is zero for self casts. At least I think so. - if (m_spellInfo->Speed > 0.0f && m_caster != target) + if (m_caster != target) { - // calculate spell incoming interval - /// @todo this is a hack - float dist = m_caster->GetDistance(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); + float hitDelay = 0.f; + WorldObject const* missileSource = m_caster; + if (m_spellInfo->HasAttribute(SPELL_ATTR4_BOUNCY_CHAIN_MISSILES)) + { + auto previousTargetItr = std::find_if(m_UniqueTargetInfo.rbegin(), m_UniqueTargetInfo.rend(), [effectMask](TargetInfo const& target) + { + return (target.EffectMask & effectMask) != 0; + }); + if (previousTargetItr != std::rend(m_UniqueTargetInfo)) + { + hitDelay = 0.0f; // this is not the first target in chain, LaunchDelay was already included - if (dist < 5.0f) - dist = 5.0f; + if (WorldObject* previousTarget = ObjectAccessor::GetWorldObject(*m_caster, previousTargetItr->TargetGUID)) + missileSource = previousTarget; - if (!m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION)) - targetInfo.TimeDelay = uint64(std::floor(dist / m_spellInfo->Speed * 1000.0f)); - else - targetInfo.TimeDelay = uint64(m_spellInfo->Speed * 1000.0f); + targetInfo.TimeDelay += previousTargetItr->TimeDelay; + } + } - // Calculate minimum incoming time - if (!m_delayMoment || m_delayMoment > targetInfo.TimeDelay) - m_delayMoment = targetInfo.TimeDelay; + if (m_spellInfo->HasAttribute(SPELL_ATTR9_SPECIAL_DELAY_CALCULATION)) + hitDelay += m_spellInfo->Speed; + else if (m_spellInfo->Speed > 0.0f) + { + // calculate spell incoming interval + /// @todo this is a hack + float dist = std::max(missileSource->GetDistance(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), 5.0f); + hitDelay += dist / m_spellInfo->Speed; + } + + targetInfo.TimeDelay += uint64(std::floor(hitDelay * 1000.0f)); } else targetInfo.TimeDelay = 0ULL; @@ -2312,6 +2332,10 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*= else targetInfo.ReflectResult = SPELL_MISS_NONE; + // Calculate minimum incoming time + if (targetInfo.TimeDelay && (!m_delayMoment || m_delayMoment > targetInfo.TimeDelay)) + m_delayMoment = targetInfo.TimeDelay; + // Add target to list m_UniqueTargetInfo.emplace_back(std::move(targetInfo)); } @@ -2736,7 +2760,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) if (caster->GetTypeId() == TYPEID_PLAYER && (procSpellType & (PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL))) { if (spell->m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || spell->m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED) - if (!spell->m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) && !spell->m_spellInfo->HasAttribute(SPELL_ATTR4_CANT_TRIGGER_ITEM_SPELLS)) + if (!spell->m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) && !spell->m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS)) caster->ToPlayer()->CastItemCombatSpell(*spellDamageInfo); } } @@ -2792,7 +2816,7 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) } // Needs to be called after dealing damage/healing to not remove breaking on damage auras - spell->DoTriggersOnSpellHit(_spellHitTarget, EffectMask); + spell->DoTriggersOnSpellHit(_spellHitTarget); } if (_enablePVP) @@ -3072,7 +3096,7 @@ void Spell::DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& hitInfo) _spellAura = nullptr; } -void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) +void Spell::DoTriggersOnSpellHit(Unit* unit) { // handle SPELL_AURA_ADD_TARGET_TRIGGER auras // this is executed after spell proc spells on target hit @@ -3083,7 +3107,7 @@ void Spell::DoTriggersOnSpellHit(Unit* unit, uint8 effMask) int32 _duration = 0; for (auto i = m_hitTriggerSpells.begin(); i != m_hitTriggerSpells.end(); ++i) { - if (CanExecuteTriggersOnHit(effMask, i->triggeredByAura) && roll_chance_i(i->chance)) + if (CanExecuteTriggersOnHit(unit, i->triggeredByAura) && roll_chance_i(i->chance)) { m_caster->CastSpell(unit, i->triggeredSpell->Id, true); TC_LOG_DEBUG("spells", "Spell %d triggered spell %d by SPELL_AURA_ADD_TARGET_TRIGGER aura", m_spellInfo->Id, i->triggeredSpell->Id); @@ -3626,7 +3650,7 @@ void Spell::_cast(bool skipCheck) SendSpellGo(); // Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells - if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled()) || m_spellInfo->HasAttribute(SPELL_ATTR4_UNK4)) + if ((m_spellInfo->Speed > 0.0f && !m_spellInfo->IsChanneled()) || m_spellInfo->HasAttribute(SPELL_ATTR4_NO_HARMFUL_THREAT)) { // Remove used for cast item if need (it can be already nullptr after TakeReagents call // in case delayed spell remove item at cast delay start @@ -6429,7 +6453,10 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint } // check if target already has the same type, but more powerful aura - if (!nonAuraEffectMask && (approximateAuraEffectMask & (1 << i)) && !m_spellInfo->IsTargetingArea()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR4_AURA_NEVER_BOUNCES) + && (!nonAuraEffectMask || m_spellInfo->HasAttribute(SPELL_ATTR4_AURA_BOUNCE_FAILS_SPELL)) + && (approximateAuraEffectMask & (1 << i)) + && !m_spellInfo->IsTargetingArea()) if (Unit* target = m_targets.GetUnitTarget()) if (!target->IsHighestExclusiveAuraEffect(m_spellInfo, AuraType(m_spellInfo->Effects[i].ApplyAuraName), m_spellInfo->Effects[i].CalcValue(m_caster, &m_spellValue->EffectBasePoints[i]), approximateAuraEffectMask, false)) @@ -6725,11 +6752,11 @@ SpellCastResult Spell::CheckArenaAndRatedBattlegroundCastRules() const if (isRatedBattleground && m_spellInfo->HasAttribute(SPELL_ATTR9_USABLE_IN_RATED_BATTLEGROUNDS)) return SPELL_CAST_OK; - if (isArena && m_spellInfo->HasAttribute(SPELL_ATTR4_USABLE_IN_ARENA)) + if (isArena && m_spellInfo->HasAttribute(SPELL_ATTR4_IGNORE_DEFAULT_ARENA_RESTRICTIONS)) return SPELL_CAST_OK; // check NOT_USABLE attributes - if (m_spellInfo->HasAttribute(SPELL_ATTR4_NOT_USABLE_IN_ARENA_OR_RATED_BG)) + if (m_spellInfo->HasAttribute(SPELL_ATTR4_NOT_IN_ARENA_OR_RATED_BATTLEGROUND)) return isArena ? SPELL_FAILED_NOT_IN_ARENA : SPELL_FAILED_NOT_IN_RATED_BATTLEGROUND; if (isArena && m_spellInfo->HasAttribute(SPELL_ATTR9_NOT_USABLE_IN_ARENA)) @@ -8381,15 +8408,20 @@ bool Spell::CheckScriptEffectImplicitTargets(uint32 effIndex, uint32 effIndexToC return true; } -bool Spell::CanExecuteTriggersOnHit(uint8 effMask, SpellInfo const* triggeredByAura) const +bool Spell::CanExecuteTriggersOnHit(Unit* unit, SpellInfo const* triggeredByAura) const { - bool only_on_caster = (triggeredByAura && triggeredByAura->HasAttribute(SPELL_ATTR4_PROC_ONLY_ON_CASTER)); - // If triggeredByAura has SPELL_ATTR4_PROC_ONLY_ON_CASTER then it can only proc on a cast spell with TARGET_UNIT_CASTER - for (uint8 i = 0;i < MAX_SPELL_EFFECTS; ++i) - { - if ((effMask & (1 << i)) && (!only_on_caster || (m_spellInfo->Effects[i].TargetA.GetTarget() == TARGET_UNIT_CASTER))) - return true; - } + bool onlyOnTarget = (triggeredByAura && (triggeredByAura->HasAttribute(SPELL_ATTR4_CLASS_TRIGGER_ONLY_ON_TARGET))); + if (!onlyOnTarget) + return true; + + // If triggeredByAura has SPELL_ATTR4_CLASS_TRIGGER_ONLY_ON_TARGET then it can only proc on either noncaster units... + if (unit != m_caster) + return true; + + // ... or caster if it is the only target + if (m_UniqueTargetInfo.size() == 1) + return true; + return false; } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 3a40044ee3..e042b631dd 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -50,6 +50,7 @@ class Item; class Object; class PathGenerator; class Player; +class SpellEffectInfo; class SpellEvent; class SpellImplicitTargetInfo; class SpellInfo; @@ -436,7 +437,7 @@ class TC_GAME_API Spell WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList = nullptr); void SearchAreaTargets(std::list& targets, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer* condList); - void SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, ConditionContainer* condList, bool isChainHeal); + void SearchChainTargets(std::list& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal); GameObject* SearchSpellFocus(); @@ -742,7 +743,7 @@ class TC_GAME_API Spell SpellMissInfo PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& targetInfo); void DoSpellEffectHit(Unit* unit, uint8 effIndex, TargetInfo& targetInfo); - void DoTriggersOnSpellHit(Unit* unit, uint8 effMask); + void DoTriggersOnSpellHit(Unit* unit); bool UpdateChanneledTargetList(); bool IsValidDeadOrAliveTarget(Unit const* target) const; void HandleLaunchPhase(); @@ -784,7 +785,7 @@ class TC_GAME_API Spell int32 chance; }; - bool CanExecuteTriggersOnHit(uint8 effMask, SpellInfo const* triggeredByAura = nullptr) const; + bool CanExecuteTriggersOnHit(Unit* unit, SpellInfo const* triggeredByAura = nullptr) const; void PrepareTriggersExecutedOnHit(); typedef std::vector HitTriggerSpellList; HitTriggerSpellList m_hitTriggerSpells; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 8fd32446aa..37698f9b67 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -5119,7 +5119,7 @@ void Spell::EffectStealBeneficialBuff(SpellEffIndex effIndex) if ((aura->GetSpellInfo()->GetDispelMask()) & dispelMask) { // Need check for passive? this - if (!aurApp->IsPositive() || aura->IsPassive() || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_NOT_STEALABLE)) + if (!aurApp->IsPositive() || aura->IsPassive() || aura->GetSpellInfo()->HasAttribute(SPELL_ATTR4_CANNOT_BE_STOLEN)) continue; // 2.4.3 Patch Notes: "Dispel effects will no longer attempt to remove effects that have 100% dispel resistance." diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 0fef531958..64310c841a 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1490,7 +1490,7 @@ bool SpellInfo::IsAutoRepeatRangedSpell() const bool SpellInfo::CausesInitialThreat() const { - return !HasAttribute(SPELL_ATTR1_NO_THREAT) && !HasAttribute(SPELL_ATTR2_NO_INITIAL_THREAT) && !HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT); + return !HasAttribute(SPELL_ATTR1_NO_THREAT) && !HasAttribute(SPELL_ATTR2_NO_INITIAL_THREAT) && !HasAttribute(SPELL_ATTR4_NO_HARMFUL_THREAT); } WeaponAttackType SpellInfo::GetAttackType() const @@ -1744,12 +1744,18 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a } // continent limitation (virtual continent) - if (HasAttribute(SPELL_ATTR4_CAST_ONLY_IN_OUTLAND)) + if (HasAttribute(SPELL_ATTR4_ONLY_FLYING_AREAS)) { - uint32 v_map = sDBCManager.GetVirtualMapForMapAndZone(map_id, zone_id); - MapEntry const* mapEntry = sMapStore.LookupEntry(v_map); + uint32 mountFlags = 0; + if (player && player->HasAuraType(SPELL_AURA_MOUNT_RESTRICTIONS)) + { + for (AuraEffect const* auraEffect : player->GetAuraEffectsByType(SPELL_AURA_MOUNT_RESTRICTIONS)) + mountFlags |= auraEffect->GetMiscValue(); + } + else if (AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(area_id)) + mountFlags = areaTable->MountFlags; - if (!mapEntry || mapEntry->ExpansionID < 1 || !mapEntry->IsContinent()) + if (!(mountFlags & AREA_MOUNT_FLAG_FLYING_ALLOWED)) return SPELL_FAILED_INCORRECT_AREA; } @@ -3526,7 +3532,7 @@ int32 SpellInfo::CalcPowerCost(WorldObject const* caster, SpellSchoolMask school } // Shiv - costs 20 + weaponSpeed*10 energy (apply only to non-triggered spell with energy cost) - if (HasAttribute(SPELL_ATTR4_SPELL_VS_EXTEND_COST)) + if (HasAttribute(SPELL_ATTR4_WEAPON_SPEED_COST_SCALING)) { uint32 speed = 0; if (SpellShapeshiftFormEntry const* ss = sSpellShapeshiftFormStore.LookupEntry(unitCaster->GetShapeshiftForm())) diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 2e073b341c..28f3d0b371 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3039,7 +3039,7 @@ void SpellMgr::LoadSpellInfoCorrections() 60864 // Jaws of Death }, [](SpellInfo* spellInfo) { - spellInfo->AttributesEx4 |= SPELL_ATTR4_FIXED_DAMAGE; + spellInfo->AttributesEx4 |= SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS; }); // Immolate @@ -3346,7 +3346,7 @@ void SpellMgr::LoadSpellInfoCorrections() ApplySpellFix({ 64085 }, [](SpellInfo* spellInfo) { // copy from similar effect of Unstable Affliction (31117) - spellInfo->AttributesEx4 |= SPELL_ATTR4_FIXED_DAMAGE; + spellInfo->AttributesEx4 |= SPELL_ATTR4_IGNORE_DAMAGE_TAKEN_MODIFIERS; spellInfo->AttributesEx6 |= SPELL_ATTR6_LIMIT_PCT_DAMAGE_MODS; }); @@ -4848,12 +4848,6 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx &= ~SPELL_ATTR1_IS_CHANNELLED; }); - // Meteor Slash - ApplySpellFix({ 88942, 95172 }, [](SpellInfo* spellInfo) - { - spellInfo->AttributesEx4 |= SPELL_ATTR4_IGNORE_RESISTANCES; - }); - // ENDOF BARADIN HOLD SPELLS //