diff options
author | ariel- <ariel-@users.noreply.github.com> | 2018-03-10 19:40:05 -0300 |
---|---|---|
committer | ariel- <ariel-@users.noreply.github.com> | 2018-03-10 19:40:19 -0300 |
commit | d6b9f148a772d07b9353a0db95a6f05c9c848a63 (patch) | |
tree | 57b08bafa579a066de8206310dc9574720fbb5b9 | |
parent | e7e46b2a822cd0179b30ba5b0fa7c6fe7cea8774 (diff) |
Core/Spell: fix target checks
- Aura will be applied at last moment possible (after damage) to prevent regressions on #18395
- Partial revert of 9b38a6352c0fe2499de54fd769aa1c721a410bda as it wasnt handling correctly checks without spells
Closes #21578
Closes #21579
Closes #21581
-rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 185 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Object.h | 7 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 38 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 4 |
4 files changed, 100 insertions, 134 deletions
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index da0b7c3ba15..37a55a2a7ea 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -2819,18 +2819,72 @@ void WorldObject::CastSpell(Position const& dest, uint32 spellId, CastSpellExtra } // function based on function Unit::CanAttack from 13850 client -bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const +bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/) const { ASSERT(target); - // can't attack self - if (this == target) + // some positive spells can be casted at hostile target + bool isPositiveSpell = bySpell && bySpell->IsPositive(); + + // can't attack self (spells can, attribute check) + if (!bySpell && this == target) + return false; + + // can't attack unattackable units + Unit const* unitTarget = target->ToUnit(); + if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE)) return false; // can't attack GMs if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) return false; + Unit const* unit = ToUnit(); + // visibility checks (only units) + if (unit) + { + // can't attack invisible + if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) + { + if (!unit->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) + return false; + } + } + + // can't attack dead + if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && unitTarget && !unitTarget->IsAlive()) + return false; + + // can't attack untargetable + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return false; + + if (Player const* playerAttacker = ToPlayer()) + { + if (playerAttacker->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_UBER)) + return false; + } + + // check flags + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)) + return false; + + // ignore immunity flags when assisting + if (isPositiveSpell && !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + { + if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC()) + return false; + + if (unitTarget && !unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC()) + return false; + + if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC()) + return false; + + if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC()) + return false; + } + // CvC case - can attack each other only when one of them is hostile if (ToUnit() && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && target->ToUnit() && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) return IsHostileTo(target) || target->IsHostileTo(this); @@ -2869,82 +2923,6 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return false; - if (!bySpell) - spellCheck = false; - - if (spellCheck && !IsValidSpellAttackTarget(target, bySpell)) - return false; - - return true; -} - -bool WorldObject::IsValidSpellAttackTarget(WorldObject const* target, SpellInfo const* bySpell) const -{ - ASSERT(target); - ASSERT(bySpell); - - // can't attack unattackable units - Unit const* unitTarget = target->ToUnit(); - if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE)) - return false; - - Unit const* unit = ToUnit(); - // visibility checks (only units) - if (unit) - { - // can't attack invisible - if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) - { - if (!unit->CanSeeOrDetect(target, bySpell->IsAffectingArea())) - return false; - - /* - else if (!obj) - { - // ignore stealth for aoe spells. Ignore stealth if target is player and unit in combat with same player - bool const ignoreStealthCheck = (bySpell && bySpell->IsAffectingArea()) || - (target->GetTypeId() == TYPEID_PLAYER && target->HasStealthAura() && IsInCombatWith(target)); - - if (!CanSeeOrDetect(target, ignoreStealthCheck)) - return false; - } - */ - } - } - - // can't attack dead - if (!bySpell->IsAllowingDeadTarget() && unitTarget && !unitTarget->IsAlive()) - return false; - - // can't attack untargetable - if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - return false; - - if (Player const* playerAttacker = ToPlayer()) - { - if (playerAttacker->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_UBER)) - return false; - } - - // check flags - if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)) - return false; - - if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC()) - return false; - - if (unitTarget && !unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC()) - return false; - - if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC()) - return false; - - if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC()) - return false; - - // check duel - before sanctuary checks - Player const* playerAffectingAttacker = unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; if (playerAffectingAttacker && playerAffectingTarget) if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0) return true; @@ -2971,41 +2949,26 @@ bool WorldObject::IsValidSpellAttackTarget(WorldObject const* target, SpellInfo } // function based on function Unit::CanAssist from 13850 client -bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const +bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell /*= nullptr*/) const { ASSERT(target); + // some negative spells can be casted at friendly target + bool isNegativeSpell = bySpell && !bySpell->IsPositive(); + // can assist to self if (this == target) return true; - // can't assist GMs - if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) - return false; - - // can't assist non-friendly targets - if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) - return false; - - if (!bySpell) - spellCheck = false; - - if (spellCheck && !IsValidSpellAssistTarget(target, bySpell)) - return false; - - return true; -} - -bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo const* bySpell) const -{ - ASSERT(target); - ASSERT(bySpell); - // can't assist unattackable units Unit const* unitTarget = target->ToUnit(); if (unitTarget && unitTarget->HasUnitState(UNIT_STATE_UNATTACKABLE)) return false; + // can't assist GMs + if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()) + return false; + // can't assist own vehicle or passenger Unit const* unit = ToUnit(); if (unit && unitTarget && unit->GetVehicle()) @@ -3018,18 +2981,22 @@ bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo } // can't assist invisible - if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE) && !CanSeeOrDetect(target, bySpell->IsAffectingArea())) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea())) return false; // can't assist dead - if (!bySpell->IsAllowingDeadTarget() && unitTarget && !unitTarget->IsAlive()) + if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && unitTarget && !unitTarget->IsAlive()) return false; // can't assist untargetable - if (!bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE) && unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + return false; + + // check flags for negative spells + if (isNegativeSpell && unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)) return false; - if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + if (isNegativeSpell || !bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) { if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { @@ -3043,6 +3010,10 @@ bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo } } + // can't assist non-friendly targets + if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) + return false; + // PvP case if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { @@ -3070,7 +3041,7 @@ bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo // !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) && else if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { - if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) + if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) if (unitTarget && !unitTarget->IsPvP()) if (Creature const* creatureTarget = target->ToCreature()) return ((creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || (creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)); diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 769e293f4ce..6d9834f8282 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -443,11 +443,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation void CastSpell(WorldObject* target, uint32 spellId, CastSpellExtraArgs const& args = { }); void CastSpell(Position const& dest, uint32 spellId, CastSpellExtraArgs const& args = { }); - bool IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const; - bool IsValidSpellAttackTarget(WorldObject const* target, SpellInfo const* bySpell) const; - - bool IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const; - bool IsValidSpellAssistTarget(WorldObject const* target, SpellInfo const* bySpell) const; + bool IsValidAttackTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr) const; + bool IsValidAssistTarget(WorldObject const* target, SpellInfo const* bySpell = nullptr) const; Unit* GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index bbd290df5a4..4293ee5e268 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2293,6 +2293,7 @@ void Spell::TargetInfo::PreprocessTarget(Spell* spell) if (missInfo != SPELL_MISS_MISS) spell->m_caster->ToUnit()->SendSpellMiss(unit, spell->m_spellInfo->Id, missInfo); spell->m_damage = 0; + spell->m_healing = 0; _spellHitTarget = nullptr; } } @@ -2555,6 +2556,20 @@ void Spell::TargetInfo::DoDamageAndTriggers(Spell* spell) else if (spell->m_caster->GetTypeId() == TYPEID_GAMEOBJECT && spell->m_caster->ToGameObject()->AI()) spell->m_caster->ToGameObject()->AI()->SpellHitTarget(_spellHitTarget, spell->m_spellInfo); + if (spell->_spellAura) + { + if (AuraApplication* aurApp = spell->_spellAura->GetApplicationOfTarget(_spellHitTarget->GetGUID())) + { + // only apply unapplied effects (for reapply case) + uint8 effMask = EffectMask & aurApp->GetEffectsToApply(); + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if ((effMask & (1 << i)) && aurApp->HasEffect(i)) + effMask &= ~(1 << i); + + _spellHitTarget->_ApplyAura(aurApp, effMask); + } + } + // Needs to be called after dealing damage/healing to not remove breaking on damage auras spell->DoTriggersOnSpellHit(_spellHitTarget, EffectMask); @@ -2643,7 +2658,7 @@ SpellMissInfo Spell::PreprocessSpellHit(Unit* unit, bool scaleAura, TargetInfo& else if (m_caster->IsFriendlyTo(unit)) { // for delayed spells ignore negative spells (after duel end) for friendly targets - if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !IsPositive() && !m_caster->IsValidSpellAttackTarget(unit, m_spellInfo)) + if (m_spellInfo->Speed > 0.0f && unit->GetTypeId() == TYPEID_PLAYER && !IsPositive() && !m_caster->IsValidAssistTarget(unit, m_spellInfo)) return SPELL_MISS_EVADE; // assisting case, healing and resurrection @@ -8005,13 +8020,13 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const case TARGET_CHECK_ENEMY: if (unitTarget->IsTotem()) return false; - if (!_caster->IsValidAttackTarget(unitTarget, _spellInfo, false)) + if (!_caster->IsValidAttackTarget(unitTarget, _spellInfo)) return false; break; case TARGET_CHECK_ALLY: if (unitTarget->IsTotem()) return false; - if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) + if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo)) return false; break; case TARGET_CHECK_PARTY: @@ -8019,7 +8034,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const return false; if (unitTarget->IsTotem()) return false; - if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) + if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo)) return false; if (!refUnit->IsInPartyWith(unitTarget)) return false; @@ -8035,7 +8050,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const return false; if (unitTarget->IsTotem()) return false; - if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo, false)) + if (!_caster->IsValidAssistTarget(unitTarget, _spellInfo)) return false; if (!refUnit->IsInRaidWith(unitTarget)) return false; @@ -8043,19 +8058,6 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const default: break; } - - // then check actual spell positivity to determine if the target is valid - // (negative spells may be targeted on allies) - if (_spellInfo->IsPositive()) - { - if (!_caster->IsValidSpellAssistTarget(unitTarget, _spellInfo)) - return false; - } - else - { - if (!_caster->IsValidSpellAttackTarget(unitTarget, _spellInfo)) - return false; - } } if (!_condSrcInfo) diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index e1a374596f8..96dec8e753b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1212,10 +1212,6 @@ void Spell::EffectApplyAura(SpellEffIndex effIndex) aurApp = unitTarget->_CreateAuraApplication(_spellAura, 1 << effIndex); else aurApp->UpdateApplyEffectMask(aurApp->GetEffectsToApply() | 1 << effIndex); - - // apply effect on target (skip for reapply) - if (!aurApp->HasEffect(effIndex)) - unitTarget->_ApplyAuraEffect(_spellAura, effIndex); } void Spell::EffectUnlearnSpecialization(SpellEffIndex effIndex) |