aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/AI/CoreAI/TotemAI.cpp7
-rw-r--r--src/server/game/Entities/Object/Object.cpp35
-rw-r--r--src/server/game/Entities/Object/Object.h15
-rw-r--r--src/server/game/Entities/Player/Player.cpp12
-rw-r--r--src/server/game/Entities/Player/Player.h2
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.cpp5
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Spells/SpellInfo.cpp10
-rw-r--r--src/server/game/Spells/SpellInfo.h1
9 files changed, 63 insertions, 26 deletions
diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp
index c3ccd343f19..eb764e26798 100644
--- a/src/server/game/AI/CoreAI/TotemAI.cpp
+++ b/src/server/game/AI/CoreAI/TotemAI.cpp
@@ -60,7 +60,12 @@ void TotemAI::UpdateAI(uint32 /*diff*/)
Unit* victim = !_victimGUID.IsEmpty() ? ObjectAccessor::GetUnit(*me, _victimGUID) : nullptr;
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
- if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim))
+ CanSeeOrDetectExtraArgs const& canSeeOrDetectExtraArgs = CanSeeOrDetectExtraArgs{
+ .IgnorePhaseShift = spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT),
+ .IncludeHiddenBySpawnTracking = spellInfo->HasAttribute(SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING),
+ .IncludeAnyPrivateObject = spellInfo->HasAttribute(SPELL_ATTR0_CU_CAN_TARGET_ANY_PRIVATE_OBJECT)
+ };
+ if (!victim || !victim->isTargetableForAttack() || !me->IsWithinDistInMap(victim, max_range) || me->IsFriendlyTo(victim) || !me->CanSeeOrDetect(victim, canSeeOrDetectExtraArgs))
{
victim = nullptr;
float extraSearchRadius = max_range > 0.0f ? EXTRA_CELL_SEARCH_RADIUS : 0.0f;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 50f6d691717..e0891e2f6d5 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1598,18 +1598,18 @@ SmoothPhasing* WorldObject::GetOrCreateSmoothPhasing()
return _smoothPhasing.get();
}
-bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool implicitDetect, bool distanceCheck, bool checkAlert) const
+bool WorldObject::CanSeeOrDetect(WorldObject const* obj, CanSeeOrDetectExtraArgs const& args /*= { }*/) const
{
if (this == obj)
return true;
- if (obj->IsNeverVisibleFor(this, implicitDetect) || CanNeverSee(obj))
+ if (obj->IsNeverVisibleFor(this, args.ImplicitDetection) || CanNeverSee(obj, args.IgnorePhaseShift))
return false;
if (obj->IsAlwaysVisibleFor(this) || CanAlwaysSee(obj))
return true;
- if (!obj->CheckPrivateObjectOwnerVisibility(this))
+ if (!args.IncludeAnyPrivateObject && !obj->CheckPrivateObjectOwnerVisibility(this))
return false;
if (SmoothPhasing const* smoothPhasing = obj->GetSmoothPhasing())
@@ -1620,7 +1620,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool implicitDetect, bo
return false;
bool corpseVisibility = false;
- if (distanceCheck)
+ if (args.DistanceCheck)
{
bool corpseCheck = false;
if (Player const* thisPlayer = ToPlayer())
@@ -1688,15 +1688,15 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool implicitDetect, bo
if (obj->IsInvisibleDueToDespawn(this))
return false;
- if (!CanDetect(obj, implicitDetect, checkAlert))
+ if (!CanDetect(obj, args.ImplicitDetection, args.AlertCheck))
return false;
return true;
}
-bool WorldObject::CanNeverSee(WorldObject const* obj) const
+bool WorldObject::CanNeverSee(WorldObject const* obj, bool ignorePhaseShift /*= false*/) const
{
- return GetMap() != obj->GetMap() || !InSamePhase(obj);
+ return GetMap() != obj->GetMap() || (!ignorePhaseShift && !InSamePhase(obj));
}
bool WorldObject::CanDetect(WorldObject const* obj, bool implicitDetect, bool checkAlert) const
@@ -3128,11 +3128,16 @@ bool WorldObject::IsValidAttackTarget(WorldObject const* target, SpellInfo const
if (unit)
{
// can't attack invisible
- if (!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT))
+ CanSeeOrDetectExtraArgs canSeeOrDetectExtraArgs;
+ if (bySpell)
{
- if (!unit->CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))
- return false;
+ canSeeOrDetectExtraArgs.ImplicitDetection = bySpell->IsAffectingArea();
+ canSeeOrDetectExtraArgs.IgnorePhaseShift = bySpell->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT);
+ canSeeOrDetectExtraArgs.IncludeHiddenBySpawnTracking = bySpell->HasAttribute(SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING);
+ canSeeOrDetectExtraArgs.IncludeAnyPrivateObject = bySpell->HasAttribute(SPELL_ATTR0_CU_CAN_TARGET_ANY_PRIVATE_OBJECT);
}
+ if (!unit->CanSeeOrDetect(target, canSeeOrDetectExtraArgs))
+ return false;
}
// can't attack dead
@@ -3288,7 +3293,15 @@ bool WorldObject::IsValidAssistTarget(WorldObject const* target, SpellInfo const
}
// can't assist invisible
- if ((!bySpell || !bySpell->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT)) && !CanSeeOrDetect(target, bySpell && bySpell->IsAffectingArea()))
+ CanSeeOrDetectExtraArgs canSeeOrDetectExtraArgs;
+ if (bySpell)
+ {
+ canSeeOrDetectExtraArgs.ImplicitDetection = bySpell->IsAffectingArea();
+ canSeeOrDetectExtraArgs.IgnorePhaseShift = bySpell->HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT);
+ canSeeOrDetectExtraArgs.IncludeHiddenBySpawnTracking = bySpell->HasAttribute(SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING);
+ canSeeOrDetectExtraArgs.IncludeAnyPrivateObject = bySpell->HasAttribute(SPELL_ATTR0_CU_CAN_TARGET_ANY_PRIVATE_OBJECT);
+ }
+ if (!CanSeeOrDetect(target, canSeeOrDetectExtraArgs))
return false;
// can't assist dead
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index c55dfe2da85..87d40c5eaa3 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -614,6 +614,17 @@ struct FindGameObjectOptions
Optional<GameobjectTypes> GameObjectType;
};
+struct CanSeeOrDetectExtraArgs
+{
+ bool ImplicitDetection = false;
+ bool IgnorePhaseShift = false;
+ bool IncludeHiddenBySpawnTracking = false;
+ bool IncludeAnyPrivateObject = false;
+
+ bool DistanceCheck = false;
+ bool AlertCheck = false;
+};
+
class TC_GAME_API WorldObject : public Object, public WorldLocation
{
protected:
@@ -733,7 +744,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
float GetGridActivationRange() const;
float GetVisibilityRange() const;
float GetSightRange(WorldObject const* target = nullptr) const;
- bool CanSeeOrDetect(WorldObject const* obj, bool implicitDetect = false, bool distanceCheck = false, bool checkAlert = false) const;
+ bool CanSeeOrDetect(WorldObject const* obj, CanSeeOrDetectExtraArgs const& args = { }) const;
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealth;
FlaggedValuesArray32<int32, uint32, StealthType, TOTAL_STEALTH_TYPES> m_stealthDetect;
@@ -943,7 +954,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void SetLocationMapId(uint32 _mapId) { m_mapId = _mapId; }
void SetLocationInstanceId(uint32 _instanceId) { m_InstanceId = _instanceId; }
- virtual bool CanNeverSee(WorldObject const* obj) const;
+ virtual bool CanNeverSee(WorldObject const* obj, bool ignorePhaseShift = false) const;
virtual bool CanAlwaysSee([[maybe_unused]] WorldObject const* /*obj*/) const { return false; }
virtual bool IsNeverVisibleFor([[maybe_unused]] WorldObject const* seer, [[maybe_unused]] bool allowServersideObjects = false) const { return !IsInWorld() || IsDestroyedObject(); }
virtual bool IsAlwaysVisibleFor([[maybe_unused]] WorldObject const* seer) const { return false; }
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 6cdce3088cd..bf63f51f465 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -22795,11 +22795,11 @@ bool Player::IsNeverVisibleFor(WorldObject const* seer, bool allowServersideObje
return false;
}
-bool Player::CanNeverSee(WorldObject const* obj) const
+bool Player::CanNeverSee(WorldObject const* obj, bool ignorePhaseShift /*= false*/) const
{
// the intent is to delay sending visible objects until client is ready for them
// some gameobjects dont function correctly if they are sent before TransportServerTime is correctly set (after CMSG_MOVE_INIT_ACTIVE_MOVER_COMPLETE)
- return !HasPlayerLocalFlag(PLAYER_LOCAL_FLAG_OVERRIDE_TRANSPORT_SERVER_TIME) || WorldObject::CanNeverSee(obj);
+ return !HasPlayerLocalFlag(PLAYER_LOCAL_FLAG_OVERRIDE_TRANSPORT_SERVER_TIME) || WorldObject::CanNeverSee(obj, ignorePhaseShift);
}
bool Player::CanAlwaysSee(WorldObject const* obj) const
@@ -22960,7 +22960,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
{
if (HaveAtClient(target))
{
- if (!CanSeeOrDetect(target, false, true))
+ if (!CanSeeOrDetect(target, { .DistanceCheck = true }))
{
switch (target->GetTypeId())
{
@@ -22991,7 +22991,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
}
else
{
- if (CanSeeOrDetect(target, false, true))
+ if (CanSeeOrDetect(target, { .DistanceCheck = true }))
{
target->SendUpdateToPlayer(this);
m_clientGUIDs.insert(target->GetGUID());
@@ -23086,7 +23086,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObjec
{
if (HaveAtClient(target))
{
- if (!CanSeeOrDetect(target, false, true))
+ if (!CanSeeOrDetect(target, { .DistanceCheck = true }))
{
BeforeVisibilityDestroy<T>(target, this);
@@ -23104,7 +23104,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObjec
}
else
{
- if (CanSeeOrDetect(target, false, true))
+ if (CanSeeOrDetect(target, { .DistanceCheck = true }))
{
target->BuildCreateUpdateBlockForPlayer(&data, this);
m_clientGUIDs.insert(target->GetGUID());
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 1c8c9c7b474..5bb8e082a42 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -3081,7 +3081,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
std::unique_ptr<Runes> m_runes;
EquipmentSetContainer _equipmentSets;
- bool CanNeverSee(WorldObject const* obj) const override;
+ bool CanNeverSee(WorldObject const* obj, bool ignorePhaseShift = false) const override;
bool CanAlwaysSee(WorldObject const* obj) const override;
bool IsAlwaysDetectableFor(WorldObject const* seer) const override;
diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp
index f004e15b564..d37613a66cb 100644
--- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp
+++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp
@@ -124,10 +124,11 @@ inline void CreatureUnitRelocationWorker(Creature* c, Unit* u)
if (!c->HasUnitState(UNIT_STATE_SIGHTLESS))
{
- if (c->IsAIEnabled() && c->CanSeeOrDetect(u, false, true))
+ if (c->IsAIEnabled() && c->CanSeeOrDetect(u, { .DistanceCheck = true }))
c->AI()->MoveInLineOfSight_Safe(u);
else
- if (u->GetTypeId() == TYPEID_PLAYER && u->HasStealthAura() && c->IsAIEnabled() && c->CanSeeOrDetect(u, false, true, true))
+ if (u->GetTypeId() == TYPEID_PLAYER && u->HasStealthAura() && c->IsAIEnabled()
+ && c->CanSeeOrDetect(u, { .DistanceCheck = true, .AlertCheck = true }))
c->AI()->TriggerAlert(u);
}
}
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 26eeb1320ef..3345c2f0b95 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -776,7 +776,7 @@ enum SpellAttr8 : uint32
SPELL_ATTR8_ONLY_TARGET_OWN_SUMMONS = 0x00010000, // TITLE Only Target Own Summons
SPELL_ATTR8_HASTE_AFFECTS_DURATION = 0x00020000, // TITLE Haste Affects Duration
SPELL_ATTR8_IGNORE_SPELLCAST_OVERRIDE_COST = 0x00040000, // TTILE Ignore Spellcast Override Cost
- SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING = 0x00080000, /*NYI - no spawn tracking implementation*/ // TITLE Allow Targets Hidden by Spawn Tracking
+ SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING = 0x00080000, // TITLE Allow Targets Hidden by Spawn Tracking
SPELL_ATTR8_REQUIRES_EQUIPPED_INV_TYPES = 0x00100000, // TITLE Requires Equipped Inv Types
SPELL_ATTR8_NO_SUMMON_DEST_FROM_CLIENT_TARGETING_PATHING_REQUIREMENT = 0x00200000, /*NYI - vald path to a spell dest is not required currently if the dest comes from client*/ // TITLE No 'Summon + Dest from Client' Targeting Pathing Requirement
SPELL_ATTR8_MELEE_HASTE_AFFECTS_PERIODIC = 0x00400000, // TITLE Melee Haste Affects Periodic
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 5100e50f278..f8e6caabb65 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -2187,8 +2187,14 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co
if (HasAttribute(SPELL_ATTR1_EXCLUDE_CASTER) && caster == target)
return SPELL_FAILED_BAD_TARGETS;
- // check visibility - ignore invisibility/stealth for implicit (area) targets
- if (!HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT) && !caster->CanSeeOrDetect(target, implicit))
+ // check visibility - Ignore invisibility/stealth for implicit (area) targets
+ CanSeeOrDetectExtraArgs const& canSeeOrDetectExtraArgs = CanSeeOrDetectExtraArgs{
+ .ImplicitDetection = implicit,
+ .IgnorePhaseShift = HasAttribute(SPELL_ATTR6_IGNORE_PHASE_SHIFT),
+ .IncludeHiddenBySpawnTracking = HasAttribute(SPELL_ATTR8_ALLOW_TARGETS_HIDDEN_BY_SPAWN_TRACKING),
+ .IncludeAnyPrivateObject = HasAttribute(SPELL_ATTR0_CU_CAN_TARGET_ANY_PRIVATE_OBJECT)
+ };
+ if (!caster->CanSeeOrDetect(target, canSeeOrDetectExtraArgs))
return SPELL_FAILED_BAD_TARGETS;
Unit const* unitTarget = target->ToUnit();
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 8d7cbefc396..4db3dceb217 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -167,6 +167,7 @@ enum SpellCustomAttributes
SPELL_ATTR0_CU_DEPRECATED_LIQUID_AURA = 0x00400000, // DO NOT REUSE
SPELL_ATTR0_CU_IS_TALENT = 0x00800000,
SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED = 0x01000000,
+ SPELL_ATTR0_CU_CAN_TARGET_ANY_PRIVATE_OBJECT = 0x02000000,
};
uint32 GetTargetFlagMask(SpellTargetObjectTypes objType);