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

(cherry picked from commit d6b9f148a772d07b9353a0db95a6f05c9c848a63)
This commit is contained in:
ariel-
2021-09-05 22:43:48 +02:00
committed by Shauren
parent 7c63a99bab
commit a2ea26816e
4 changed files with 107 additions and 143 deletions
+85 -116
View File
@@ -2661,12 +2661,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
@@ -2674,10 +2682,56 @@ 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->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
return false;
if (Player const* playerAttacker = ToPlayer())
{
if (playerAttacker->HasPlayerFlag(PLAYER_FLAGS_UBER))
return false;
}
// check flags
if (unitTarget && unitTarget->HasUnitFlag(UnitFlags(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->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC())
return false;
if (unitTarget && !unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC())
return false;
if (!bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
{
if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC())
return false;
if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC())
return false;
}
}
// CvC case - can attack each other only when one of them is hostile
if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && targetUnit && !targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && !unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
return IsHostileTo(target) || target->IsHostileTo(this);
// PvP, PvC, CvP case
@@ -2686,14 +2740,14 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const
return false;
Player const* playerAffectingAttacker = unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
Player const* playerAffectingTarget = targetUnit && targetUnit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
Player const* playerAffectingTarget = unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? 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 ? unitTarget : unit)
{
if (creature->IsContestedGuard() && player->HasPlayerFlag(PLAYER_FLAGS_CONTESTED_PVP))
return true;
@@ -2714,85 +2768,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->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
return false;
if (Player const* playerAttacker = ToPlayer())
{
if (playerAttacker->HasPlayerFlag(PLAYER_FLAGS_UBER))
return false;
}
// check flags
if (unitTarget && unitTarget->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)))
return false;
if (unit && !unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToNPC())
return false;
if (unitTarget && !unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToNPC())
return false;
if (!bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
{
if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unitTarget && unitTarget->IsImmuneToPC())
return false;
if (unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && unit && unit->IsImmuneToPC())
return false;
}
// check duel - before sanctuary checks
Player const* playerAffectingAttacker = unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
Player const* playerAffectingTarget = unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
if (playerAffectingAttacker && playerAffectingTarget)
if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
return true;
@@ -2819,41 +2794,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())
@@ -2866,23 +2826,28 @@ 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->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && unitTarget && unitTarget->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
return false;
if (!bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
// check flags for negative spells
if (isNegativeSpell && unitTarget && unitTarget->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)))
return false;
if (isNegativeSpell || !bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
{
if (unit && unit->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
{
if (unitTarget && unitTarget->IsImmuneToPC())
return false;
if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
if (unitTarget && unitTarget->IsImmuneToPC())
return false;
}
else
{
@@ -2891,6 +2856,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->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE))
{
@@ -2918,7 +2887,7 @@ bool WorldObject::IsValidSpellAssistTarget(WorldObject const* target, SpellInfo
// !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) &&
else if (unit && unit->HasUnitFlag(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));