diff options
| author | ariel- <ariel-@users.noreply.github.com> | 2018-01-23 01:40:58 -0300 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2021-08-28 15:59:11 +0200 |
| commit | 52ae3c89d1d4dbcff309d940fd41c7212edc77b7 (patch) | |
| tree | dc7940d0ea54ed3cad40edbf1df48a622860593c /src/server/game/Entities | |
| parent | 1e1415a49128d034c8d48aa8cbb5d157200371b0 (diff) | |
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
Closes #8844
(cherry picked from commit 9b38a6352c0fe2499de54fd769aa1c721a410bda)
Diffstat (limited to 'src/server/game/Entities')
| -rw-r--r-- | src/server/game/Entities/AreaTrigger/AreaTrigger.cpp | 4 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 190 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 8 | ||||
| -rw-r--r-- | src/server/game/Entities/Unit/UnitDefines.h | 4 |
4 files changed, 113 insertions, 93 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index e389d58d543..773226e43fe 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -605,11 +605,11 @@ bool UnitFitToActionRequirement(Unit* unit, Unit* caster, AreaTriggerAction cons { case AREATRIGGER_ACTION_USER_FRIEND: { - return caster->_IsValidAssistTarget(unit, sSpellMgr->GetSpellInfo(action.Param, caster->GetMap()->GetDifficultyID())); + return caster->IsValidAssistTarget(unit, sSpellMgr->GetSpellInfo(action.Param, caster->GetMap()->GetDifficultyID())); } case AREATRIGGER_ACTION_USER_ENEMY: { - return caster->_IsValidAttackTarget(unit, sSpellMgr->GetSpellInfo(action.Param, caster->GetMap()->GetDifficultyID())); + return caster->IsValidAttackTarget(unit, sSpellMgr->GetSpellInfo(action.Param, caster->GetMap()->GetDifficultyID())); } case AREATRIGGER_ACTION_USER_RAID: { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 94cbeaf9bc0..fadd871a970 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -6540,7 +6540,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->HasHitDelay()) @@ -6571,7 +6571,7 @@ Unit* Unit::GetMeleeHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo / for (AuraEffectList::const_iterator i = interceptAuras.begin(); i != interceptAuras.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))) { @@ -8270,13 +8270,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); @@ -8284,9 +8279,56 @@ 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 (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && !target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + 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 = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = target->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; + Unit const* creature = playerAffectingAttacker ? target : this; + + if (creature->IsContestedGuard() && player->HasPlayerFlag(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.HasFlag(ReputationFlags::AtWar)) + 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 @@ -8312,7 +8354,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)) @@ -8326,56 +8368,28 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo } // check flags - if (target->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16)) - || (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToNPC()) - || (!target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToNPC())) + if (target->HasUnitFlag(UnitFlags(UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_TAXI_FLIGHT | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_UNK_16))) return false; - if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG)) - && (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && 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 (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && target->IsImmuneToNPC()) return false; - // CvC case - can attack each other only when one of them is hostile - if (!HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && !target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) - 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->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToNPC()) return false; - Player const* playerAffectingAttacker = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; - Player const* playerAffectingTarget = target->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; - Unit const* creature = playerAffectingAttacker ? target : this; - - if (creature->IsContestedGuard() && player->HasPlayerFlag(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.HasFlag(ReputationFlags::AtWar)) - return false; + if ((!bySpell || bySpell->HasAttribute(SPELL_ATTR8_ATTACK_IGNORE_IMMUNE_TO_PC_FLAG)) + && HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && 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->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) && IsImmuneToPC()) + if (GetEntry() != WORLD_TRIGGER && (!obj || !obj->isType(TYPEMASK_GAMEOBJECT | TYPEMASK_DYNAMICOBJECT))) + return false; - Creature const* creatureAttacker = ToCreature(); - if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) - return false; // check duel - before sanctuary checks + Player const* playerAffectingAttacker = HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr; + Player const* playerAffectingTarget = target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr; if (playerAffectingAttacker && playerAffectingTarget) if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0) return true; @@ -8397,16 +8411,12 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo return HasPvpFlag(UNIT_BYTE2_FLAG_UNK1) || target->HasPvpFlag(UNIT_BYTE2_FLAG_UNK1); } - return true; -} -bool Unit::IsValidAssistTarget(Unit const* target) const -{ - return _IsValidAssistTarget(target, nullptr); + return true; } // 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); @@ -8414,27 +8424,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->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_CAN_TARGET_UNTARGETABLE)) && target->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) return false; if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) @@ -8451,17 +8480,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->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) + if (target->HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) { Player const* targetPlayerOwner = target->GetAffectingPlayer(); if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) @@ -8470,29 +8490,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->HasPvpFlag(UNIT_BYTE2_FLAG_FFA_PVP) - && !HasPvpFlag(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->HasPvpFlag(UNIT_BYTE2_FLAG_PVP)) + 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 (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE) - && (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_ASSIST_IGNORE_IMMUNE_FLAG)) - && !target->HasPvpFlag(UNIT_BYTE2_FLAG_PVP)) + else if (HasUnitFlag(UNIT_FLAG_PVP_ATTACKABLE)) { - 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; } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index da63f0558c7..a2bb7f1a301 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1161,11 +1161,11 @@ class TC_GAME_API Unit : public WorldObject bool isTargetableForAttack(bool checkFakeDeath = true) const; - bool IsValidAttackTarget(Unit const* target) const; - bool _IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj = nullptr) const; + bool IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell = nullptr, WorldObject const* obj = nullptr, bool spellCheck = true) const; + bool IsValidSpellAttackTarget(Unit const* target, SpellInfo const* bySpell, WorldObject const* obj = nullptr) const; - bool IsValidAssistTarget(Unit const* target) const; - bool _IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) const; + bool IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell = nullptr, bool spellCheck = true) const; + bool IsValidSpellAssistTarget(Unit const* target, SpellInfo const* bySpell) const; virtual bool IsInWater() const; virtual bool IsUnderWater() const; diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index b0191780ae8..fac8eda8d46 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -124,8 +124,8 @@ enum UnitFlags : uint32 UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP UNIT_FLAG_UNK_6 = 0x00000040, UNIT_FLAG_NOT_ATTACKABLE_1 = 0x00000080, // ?? (UNIT_FLAG_PVP_ATTACKABLE | UNIT_FLAG_NOT_ATTACKABLE_1) is NON_PVP_ATTACKABLE - UNIT_FLAG_IMMUNE_TO_PC = 0x00000100, // disables combat/assistance with PlayerCharacters (PC) - see Unit::_IsValidAttackTarget, Unit::_IsValidAssistTarget - UNIT_FLAG_IMMUNE_TO_NPC = 0x00000200, // disables combat/assistance with NonPlayerCharacters (NPC) - see Unit::_IsValidAttackTarget, Unit::_IsValidAssistTarget + UNIT_FLAG_IMMUNE_TO_PC = 0x00000100, // disables combat/assistance with PlayerCharacters (PC) - see Unit::IsValidAttackTarget, Unit::IsValidAssistTarget + UNIT_FLAG_IMMUNE_TO_NPC = 0x00000200, // disables combat/assistance with NonPlayerCharacters (NPC) - see Unit::IsValidAttackTarget, Unit::IsValidAssistTarget UNIT_FLAG_LOOTING = 0x00000400, // loot animation UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // on player pets: whether the pet is chasing a target to attack || on other units: whether any of the unit's minions is in combat UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3 |
