diff options
author | Meji <alvaro.megias@outlook.com> | 2025-05-11 21:10:21 +0200 |
---|---|---|
committer | Ovahlord <dreadkiller@gmx.de> | 2025-05-26 20:49:44 +0200 |
commit | c093df0f9cc62c3be2f7d638d3eeb671601e0568 (patch) | |
tree | e55fcec841e40df7d13cbbea4698f3ffccbff70c | |
parent | 85f9d08932255e4c013c046a10efca2bbde54878 (diff) |
Core/Misc: Added new CanSeeOrDetectExtraArgs to CanSeeOrDetect (#30882)
(cherry picked from commit 260fab23786917619ad3453677ed7d983a431cc4)
# Conflicts:
# src/server/game/Entities/Object/Object.cpp
-rw-r--r-- | src/server/game/AI/CoreAI/TotemAI.cpp | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 35 | ||||
-rw-r--r-- | src/server/game/Entities/Object/Object.h | 15 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 12 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 | ||||
-rw-r--r-- | src/server/game/Grids/Notifiers/GridNotifiers.cpp | 5 | ||||
-rw-r--r-- | src/server/game/Miscellaneous/SharedDefines.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 10 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 1 |
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); |