Core/Spells: rework part 2: Split up target selection logic
Target check only determines what kind of entities we can target, spell positivity determines if that entity is valid for attack/assist
This commit is contained in:
1 parent
95bc5dcc40
commit
98a988a77a
6 files changed
+140
-110
No files matched your search
@@ -6390,7 +6390,7 @@ Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
|
||||
{
|
||||
if (Unit* magnet = (*itr)->GetBase()->GetCaster())
|
||||
if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK
|
||||
&& _IsValidAttackTarget(magnet, spellInfo))
|
||||
&& IsValidAttackTarget(magnet, spellInfo))
|
||||
{
|
||||
/// @todo handle this charge drop by proc in cast phase on explicit target
|
||||
if (spellInfo->Speed > 0.0f)
|
||||
@@ -6415,7 +6415,7 @@ Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
|
||||
for (AuraEffectList::const_iterator i = hitTriggerAuras.begin(); i != hitTriggerAuras.end(); ++i)
|
||||
{
|
||||
if (Unit* magnet = (*i)->GetBase()->GetCaster())
|
||||
if (_IsValidAttackTarget(magnet, spellInfo) && magnet->IsWithinLOSInMap(this)
|
||||
if (IsValidAttackTarget(magnet, spellInfo) && magnet->IsWithinLOSInMap(this)
|
||||
&& (!spellInfo || (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK
|
||||
&& spellInfo->CheckTarget(this, magnet, false) == SPELL_CAST_OK)))
|
||||
if (roll_chance_i((*i)->GetAmount()))
|
||||
@@ -8484,13 +8484,8 @@ bool Unit::isTargetableForAttack(bool checkFakeDeath) const
|
||||
return !HasUnitState(UNIT_STATE_UNATTACKABLE) && (!checkFakeDeath || !HasUnitState(UNIT_STATE_DIED));
|
||||
}
|
||||
|
||||
bool Unit::IsValidAttackTarget(Unit const* target) const
|
||||
{
|
||||
return _IsValidAttackTarget(target, nullptr);
|
||||
}
|
||||
|
||||
// function based on function Unit::CanAttack from 13850 client
|
||||
bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj) const
|
||||
bool Unit::IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell /*= nullptr*/, WorldObject const* obj /*= nullptr*/, bool spellCheck /*= true*/) const
|
||||
{
|
||||
ASSERT(target);
|
||||
|
||||
@@ -8498,9 +8493,55 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|
||||
if (this == target)
|
||||
return false;
|
||||
|
||||
// can't attack unattackable units or GMs
|
||||
if (target->HasUnitState(UNIT_STATE_UNATTACKABLE)
|
||||
|| (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()))
|
||||
// can't attack GMs
|
||||
if (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster())
|
||||
return false;
|
||||
|
||||
// CvC case - can attack each other only when one of them is hostile
|
||||
if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
return IsHostileTo(target) || target->IsHostileTo(this);
|
||||
|
||||
// PvP, PvC, CvP case
|
||||
// can't attack friendly targets
|
||||
if (IsFriendlyTo(target) || target->IsFriendlyTo(this))
|
||||
return false;
|
||||
|
||||
Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr;
|
||||
Player const* playerAffectingTarget = 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;
|
||||
Unit const* creature = playerAffectingAttacker ? target : this;
|
||||
|
||||
if (creature->IsContestedGuard() && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
|
||||
return true;
|
||||
|
||||
if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
|
||||
{
|
||||
if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
|
||||
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction))
|
||||
if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
|
||||
if (!(repState->Flags & FACTION_FLAG_AT_WAR))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Creature const* creatureAttacker = ToCreature();
|
||||
if (creatureAttacker && (creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))
|
||||
return false;
|
||||
|
||||
if (spellCheck && !IsValidSpellAttackTarget(target, bySpell, obj))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unit::IsValidSpellAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj /*= nullptr*/) const
|
||||
{
|
||||
// can't attack unattackable units
|
||||
if (target->HasUnitState(UNIT_STATE_UNATTACKABLE))
|
||||
return false;
|
||||
|
||||
// visibility checks
|
||||
@@ -8526,7 +8567,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|
||||
|
||||
// can't attack dead
|
||||
if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive())
|
||||
return false;
|
||||
return false;
|
||||
|
||||
// can't attack untargetable
|
||||
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE))
|
||||
@@ -8540,64 +8581,34 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|
||||
}
|
||||
|
||||
// check flags
|
||||
if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_NOT_ATTACKABLE_2)
|
||||
|| (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && target->IsImmuneToNPC())
|
||||
|| (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && IsImmuneToNPC()))
|
||||
if (target->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 ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG))
|
||||
&& (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && target->IsImmuneToPC())
|
||||
// check if this is a world trigger cast - GOs are using world triggers to cast their spells, so we need to ignore their immunity flag here, this is a temp workaround, needs removal when go cast is implemented properly
|
||||
&& GetEntry() != WORLD_TRIGGER)
|
||||
if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && target->IsImmuneToNPC())
|
||||
return false;
|
||||
|
||||
// CvC case - can attack each other only when one of them is hostile
|
||||
if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
return GetReactionTo(target) <= REP_HOSTILE || target->GetReactionTo(this) <= REP_HOSTILE;
|
||||
|
||||
// PvP, PvC, CvP case
|
||||
// can't attack friendly targets
|
||||
if (GetReactionTo(target) > REP_NEUTRAL
|
||||
|| target->GetReactionTo(this) > REP_NEUTRAL)
|
||||
if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && IsImmuneToNPC())
|
||||
return false;
|
||||
|
||||
Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr;
|
||||
Player const* playerAffectingTarget = 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;
|
||||
Unit const* creature = playerAffectingAttacker ? target : this;
|
||||
|
||||
if (creature->IsContestedGuard() && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
|
||||
return true;
|
||||
|
||||
if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
|
||||
{
|
||||
if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
|
||||
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->Faction))
|
||||
if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
|
||||
if (!(repState->Flags & FACTION_FLAG_AT_WAR))
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Creature const* creatureAttacker = ToCreature();
|
||||
if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)
|
||||
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && target->IsImmuneToPC())
|
||||
return false;
|
||||
|
||||
// check if this is a world trigger cast - GOs are using world triggers to cast their spells, so we need to ignore their immunity flag here, this is a temp workaround, needs removal when go cast is implemented properly
|
||||
if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && IsImmuneToPC())
|
||||
if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT)))
|
||||
return false;
|
||||
|
||||
|
||||
// check duel - before sanctuary checks
|
||||
Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) ? GetAffectingPlayer() : nullptr;
|
||||
Player const* playerAffectingTarget = target->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;
|
||||
|
||||
// PvP case - can't attack when attacker or target are in sanctuary
|
||||
// however, 13850 client doesn't allow to attack when one of the unit's has sanctuary flag and is pvp
|
||||
if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)
|
||||
&& ((target->GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_SANCTUARY) || (GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_SANCTUARY)))
|
||||
if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED) && (target->IsInSanctuary() || IsInSanctuary()))
|
||||
return false;
|
||||
|
||||
// additional checks - only PvP case
|
||||
@@ -8612,16 +8623,12 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|
||||
return HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_UNK1)
|
||||
|| target->HasByteFlag(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, UNIT_BYTE2_FLAG_UNK1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unit::IsValidAssistTarget(Unit const* target) const
|
||||
{
|
||||
return _IsValidAssistTarget(target, nullptr);
|
||||
}
|
||||
|
||||
// function based on function Unit::CanAssist from 13850 client
|
||||
bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const
|
||||
bool Unit::IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell /*= nullptr*/, bool spellCheck /*= true*/) const
|
||||
{
|
||||
ASSERT(target);
|
||||
|
||||
@@ -8629,27 +8636,46 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
|
||||
if (this == target)
|
||||
return true;
|
||||
|
||||
// can't assist unattackable units or GMs
|
||||
if (target->HasUnitState(UNIT_STATE_UNATTACKABLE)
|
||||
|| (target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->IsGameMaster()))
|
||||
// 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 (spellCheck && !IsValidSpellAssistTarget(target, bySpell))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unit::IsValidSpellAssistTarget(Unit const* target, SpellInfo const* bySpell) const
|
||||
{
|
||||
// can't assist unattackable units
|
||||
if (target->HasUnitState(UNIT_STATE_UNATTACKABLE))
|
||||
return false;
|
||||
|
||||
// can't assist own vehicle or passenger
|
||||
if (m_vehicle)
|
||||
if (IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))
|
||||
{
|
||||
if (IsOnVehicle(target))
|
||||
return false;
|
||||
|
||||
if (m_vehicle->GetBase()->IsOnVehicle(target))
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't assist invisible
|
||||
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_INVISIBLE)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))
|
||||
return false;
|
||||
|
||||
// can't assist dead
|
||||
if ((!bySpell || !bySpell->IsAllowingDeadTarget()) && !target->IsAlive())
|
||||
return false;
|
||||
return false;
|
||||
|
||||
// can't assist untargetable
|
||||
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE))
|
||||
&& target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
|
||||
if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
|
||||
return false;
|
||||
|
||||
if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
|
||||
@@ -8666,18 +8692,8 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction)
|
||||
if (GetTypeId() == TYPEID_PLAYER && IsCharmed() && GetCharmerGUID().IsCreature())
|
||||
return true;
|
||||
|
||||
// PvP case
|
||||
else if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
{
|
||||
Player const* targetPlayerOwner = target->GetAffectingPlayer();
|
||||
if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
@@ -8686,29 +8702,29 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co
|
||||
if (selfPlayerOwner && targetPlayerOwner)
|
||||
{
|
||||
// can't assist player which is dueling someone
|
||||
if (selfPlayerOwner != targetPlayerOwner
|
||||
&& targetPlayerOwner->duel)
|
||||
if (selfPlayerOwner != targetPlayerOwner && targetPlayerOwner->duel)
|
||||
return false;
|
||||
}
|
||||
// can't assist player in ffa_pvp zone from outside
|
||||
if ((target->GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_FFA_PVP)
|
||||
&& !(GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_FFA_PVP))
|
||||
if (target->IsFFAPvP() && !IsFFAPvP())
|
||||
return false;
|
||||
|
||||
// can't assist player out of sanctuary from sanctuary if has pvp enabled
|
||||
if (target->GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_PVP)
|
||||
if ((GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_SANCTUARY) && !(target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_SANCTUARY))
|
||||
if (target->IsPvP())
|
||||
if (IsInSanctuary() && !target->IsInSanctuary())
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// PvC case - player can assist creature only if has specific type flags
|
||||
// !target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) &&
|
||||
else if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED)
|
||||
&& (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
|
||||
&& !((target->GetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG) & UNIT_BYTE2_FLAG_PVP)))
|
||||
else if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED))
|
||||
{
|
||||
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;
|
||||
if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG))
|
||||
if (!target->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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user