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
This commit is contained in:
ariel- authored and Ovahlord committed 2022-09-12 09:15:15 +02:00
1 parent 5e6167ce46
commit c889b81f2c
4 files changed
+101 -138

No files matched your search

+79 -111
View File
@@ -2946,12 +2946,20 @@ 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
@@ -2959,10 +2967,53 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const
return false;
Unit const* unit = ToUnit();
Unit const* targetUnit = target->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_NOT_ATTACKABLE_2))
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_PLAYER_CONTROLLED) && unitTarget && unitTarget->IsImmuneToNPC())
return false;
if (unitTarget && !unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unit && unit->IsImmuneToNPC())
return false;
if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unitTarget && unitTarget->IsImmuneToPC())
return false;
if (unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unit && unit->IsImmuneToPC())
return false;
}
// CvC case - can attack each other only when one of them is hostile
if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && targetUnit && !targetUnit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
if (ToUnit() && !HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && target->ToUnit() && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
return IsHostileTo(target) || target->IsHostileTo(this);
// PvP, PvC, CvP case
@@ -2970,15 +3021,15 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const
if (IsFriendlyTo(target) || target->IsFriendlyTo(this))
return false;
Player const* playerAffectingAttacker = unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr;
Player const* playerAffectingTarget = targetUnit && targetUnit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? target->GetAffectingPlayer() : nullptr;
Player const* playerAffectingAttacker = ToUnit() && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr;
Player const* playerAffectingTarget = target->ToUnit() && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? target->GetAffectingPlayer() : nullptr;
// Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
if ((playerAffectingAttacker && !playerAffectingTarget) || (!playerAffectingAttacker && playerAffectingTarget))
{
Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
if (Unit const* creature = playerAffectingAttacker ? targetUnit : unit)
if (Unit const* creature = playerAffectingAttacker ? target->ToUnit() : ToUnit())
{
if (creature->IsContestedGuard() && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
return true;
@@ -2998,83 +3049,7 @@ 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 || !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_NOT_ATTACKABLE_2))
return false;
if (unit && !unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unitTarget->IsImmuneToNPC())
return false;
if (!bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
{
if (unitTarget && !unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unit && unit->IsImmuneToNPC())
return false;
if (unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && unitTarget->IsImmuneToPC())
return false;
}
// check duel - before sanctuary checks
Player const* playerAffectingAttacker = unit && unit->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr;
Player const* playerAffectingTarget = unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? target->GetAffectingPlayer() : nullptr;
if (playerAffectingAttacker && playerAffectingTarget)
if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
return true;
@@ -3101,36 +3076,17 @@ 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();
@@ -3138,6 +3094,10 @@ bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo
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())
@@ -3150,18 +3110,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 || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && unitTarget && unitTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
return false;
if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
// 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_NOT_ATTACKABLE_2))
return false;
if (isNegativeSpell || !bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
{
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
{
@@ -3175,6 +3139,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_PLAYER_CONTROLLED))
{
@@ -3202,7 +3170,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_PLAYER_CONTROLLED))
{
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));
+2 -5
View File
@@ -468,11 +468,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);
+20 -18
View File
@@ -2452,6 +2452,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;
}
}
@@ -2736,6 +2737,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);
@@ -2838,7 +2853,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
@@ -8528,13 +8543,13 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const
case TARGET_CHECK_ENEMY:
if (unitTarget->IsTotem())
return false;
if (!target->IsCorpse() && !_caster->IsValidAttackTarget(unitTarget, _spellInfo, false))
if (!target->IsCorpse() && !_caster->IsValidAttackTarget(unitTarget, _spellInfo))
return false;
break;
case TARGET_CHECK_ALLY:
if (unitTarget->IsTotem())
return false;
if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo, false))
if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo))
return false;
break;
case TARGET_CHECK_PARTY:
@@ -8542,7 +8557,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const
return false;
if (unitTarget->IsTotem())
return false;
if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo, false))
if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo))
return false;
if (!refUnit->IsInPartyWith(unitTarget))
return false;
@@ -8558,7 +8573,7 @@ bool WorldObjectSpellTargetCheck::operator()(WorldObject* target) const
return false;
if (unitTarget->IsTotem())
return false;
if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo, false))
if (!target->IsCorpse() && !_caster->IsValidAssistTarget(unitTarget, _spellInfo))
return false;
if (!refUnit->IsInRaidWith(unitTarget))
return false;
@@ -8582,19 +8597,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)
-4
View File
@@ -953,10 +953,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)