diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index 3b9eb27d79..b1d3428e45 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -133,8 +133,23 @@ SpellCastResult UnitAI::DoCast(uint32 spellId) { if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) { - bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER); - target = SelectTarget(SELECT_TARGET_RANDOM, 0, spellInfo->GetMaxRange(false), playerOnly); + DefaultTargetSelector targetSelectorInner(me, spellInfo->GetMaxRange(false), false, true, 0); + auto targetSelector = [&](Unit const* candidate) -> bool + { + if (!candidate->IsPlayer()) + { + if (spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER)) + return false; + + if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && candidate->IsControlledByPlayer()) + return false; + } + else if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) + return false; + + return targetSelectorInner(candidate); + }; + target = SelectTarget(SELECT_TARGET_RANDOM, 0, targetSelector); } break; } @@ -148,10 +163,24 @@ SpellCastResult UnitAI::DoCast(uint32 spellId) { if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId)) { - bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER); float range = spellInfo->GetMaxRange(false); - DefaultTargetSelector targetSelector(me, range, playerOnly, true, -(int32)spellId); + DefaultTargetSelector targetSelectorInner(me, range, false, true, -(int32)spellId); + auto targetSelector = [&](Unit const* candidate) -> bool + { + if (!candidate->IsPlayer()) + { + if (spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER)) + return false; + + if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && candidate->IsControlledByPlayer()) + return false; + } + else if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) + return false; + + return targetSelectorInner(candidate); + }; if (!spellInfo->HasAuraInterruptFlag(SpellAuraInterruptFlags::NOT_VICTIM) && targetSelector(me->GetVictim())) target = me->GetVictim(); else diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index df2afa3bfc..a7b9bc5a69 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -1065,23 +1065,26 @@ namespace Trinity AnyAoETargetUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range, SpellInfo const* spellInfo = nullptr, bool incOwnRadius = true, bool incTargetRadius = true) : i_obj(obj), i_funit(funit), _spellInfo(spellInfo), i_range(range), i_incOwnRadius(incOwnRadius), i_incTargetRadius(incTargetRadius) { - Unit const* check = i_funit; - Unit const* owner = i_funit->GetOwner(); - if (owner) - check = owner; - i_targetForPlayer = (check->GetTypeId() == TYPEID_PLAYER); } bool operator()(Unit* u) { - // Check contains checks for: live, non-selectable, non-attackable flags, flight check and GM check, ignore totems + // Check contains checks for: live, uninteractible, non-attackable flags, flight check and GM check, ignore totems if (u->GetTypeId() == TYPEID_UNIT && u->IsTotem()) return false; - if (_spellInfo && _spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER) && u->GetTypeId() != TYPEID_PLAYER) - return false; + if (_spellInfo) + { + if (!u->IsPlayer()) + { + if (_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER)) + return false; - if (_spellInfo && _spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER) && u->GetTypeId() == TYPEID_PLAYER) - return false; + if (_spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && u->IsControlledByPlayer()) + return false; + } + else if (_spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) + return false; + } if (!i_funit->IsValidAttackTarget(u, _spellInfo)) return false; @@ -1095,7 +1098,6 @@ namespace Trinity return u->IsInMap(i_obj) && u->IsInPhase(i_obj) && u->IsWithinDoubleVerticalCylinder(i_obj, searchRadius, searchRadius); } private: - bool i_targetForPlayer; WorldObject const* i_obj; Unit const* i_funit; SpellInfo const* _spellInfo; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 03ecafc230..20cb9c1f37 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -541,8 +541,8 @@ enum SpellAttr5 : uint32 SPELL_ATTR5_TRIGGERS_CHANNELING = 0x00000010, // TITLE Triggers Channeling SPELL_ATTR5_LIMIT_N = 0x00000020, // TITLE Limit N DESCRIPTION Remove previous application to another unit if applied SPELL_ATTR5_IGNORE_AREA_EFFECT_PVP_CHECK = 0x00000040, // TITLE Ignore Area Effect PvP Check - SPELL_ATTR5_NOT_ON_PLAYER = 0x00000080, /*NYI*/ // TITLE Not On Player - SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC = 0x00000100, /*NYI*/ // TITLE Not On Player Controlled NPC + SPELL_ATTR5_NOT_ON_PLAYER = 0x00000080, // TITLE Not On Player + SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC = 0x00000100, // TITLE Not On Player Controlled NPC SPELL_ATTR5_EXTRA_INITIAL_PERIOD = 0x00000200, // TITLE Extra Initial Period DESCRIPTION Immediately do periodic tick on apply SPELL_ATTR5_DO_NOT_DISPLAY_DURATION = 0x00000400, // TITLE Do Not Display Duration SPELL_ATTR5_IMPLIED_TARGETING = 0x00000800, // TITLE Implied Targeting (client only) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 2f523e2d08..f74fc77c40 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1966,7 +1966,7 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionConta if (m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_GHOSTS)) retMask &= GRID_MAP_TYPE_MASK_PLAYER; if (m_spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) - retMask &= ~(GRID_MAP_TYPE_MASK_CORPSE | GRID_MAP_TYPE_MASK_PLAYER); + retMask &= ~GRID_MAP_TYPE_MASK_PLAYER; if (condList) retMask &= sConditionMgr->GetSearcherTypeMaskForConditionList(*condList); diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index b164602de5..70d9d1a872 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1942,8 +1942,16 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co else return SPELL_CAST_OK; // corpseOwner and unit specific target checks - if (HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER) && unitTarget->GetTypeId() != TYPEID_PLAYER) - return SPELL_FAILED_TARGET_NOT_PLAYER; + if (!unitTarget->IsPlayer()) + { + if (HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER)) + return SPELL_FAILED_TARGET_NOT_PLAYER; + + if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && unitTarget->IsControlledByPlayer()) + return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED; + } + else if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER)) + return SPELL_FAILED_TARGET_IS_PLAYER; if (!IsAllowingDeadTarget() && !unitTarget->IsAlive()) return SPELL_FAILED_TARGETS_DEAD;