From 1f80df2465c1083cc062a005568aad624036d343 Mon Sep 17 00:00:00 2001 From: Hondacrx Date: Thu, 14 Aug 2025 20:37:45 -0400 Subject: [PATCH] Core/Misc: Added new CanSeeOrDetectExtraArgs to CanSeeOrDetect Port From (https://github.com/TrinityCore/TrinityCore/commit/260fab23786917619ad3453677ed7d983a431cc4) --- .../Framework/Constants/Spells/SpellConst.cs | 5 +- Source/Game/AI/CoreAI/TotemAI.cs | 7 ++- Source/Game/Entities/Object/WorldObject.cs | 60 ++++++++++++++----- Source/Game/Entities/Player/Player.cs | 12 ++-- Source/Game/Maps/GridNotifiers.cs | 4 +- Source/Game/Spells/SpellInfo.cs | 11 +++- 6 files changed, 70 insertions(+), 29 deletions(-) diff --git a/Source/Framework/Constants/Spells/SpellConst.cs b/Source/Framework/Constants/Spells/SpellConst.cs index 431bae726..c1637d228 100644 --- a/Source/Framework/Constants/Spells/SpellConst.cs +++ b/Source/Framework/Constants/Spells/SpellConst.cs @@ -2035,7 +2035,7 @@ namespace Framework.Constants OnlyTargetOwnSummons = 0x10000, // Only Target Own Summons HasteAffectsDuration = 0x20000, // Haste Affects Duration IgnoreSpellcastOverrideCost = 0x40000, // Ttile Ignore Spellcast Override Cost - AllowTargetsHiddenBySpawnTracking = 0x80000, /*Nyi - No Spawn Tracking Implementation*/ // Allow Targets Hidden By Spawn Tracking + AllowTargetsHiddenBySpawnTracking = 0x80000, // Allow Targets Hidden By Spawn Tracking RequiresEquippedInvTypes = 0x100000, // Requires Equipped Inv Types NoSummonDestFromClientTargetingPathingRequirement = 0x200000, /*Nyi - Vald Path To A Spell Dest Is Not Required Currently If The Dest Comes From Client*/ // No 'Summon + Dest From Client' Targeting Pathing Requirement MeleeHasteAffectsPeriodic = 0x400000, // Melee Haste Affects Periodic @@ -2320,7 +2320,8 @@ namespace Framework.Constants SchoolmaskNormalWithMagic = 0x200000, DeprecatedLiquidAura = 0x400000, IsTalent = 0x800000, - AuraCannotBeSaved = 0x1000000 + AuraCannotBeSaved = 0x1000000, + CanTargetAnyPrivateObject = 0x2000000, } #endregion diff --git a/Source/Game/AI/CoreAI/TotemAI.cs b/Source/Game/AI/CoreAI/TotemAI.cs index 4381c9621..69778e48c 100644 --- a/Source/Game/AI/CoreAI/TotemAI.cs +++ b/Source/Game/AI/CoreAI/TotemAI.cs @@ -38,7 +38,12 @@ namespace Game.AI Unit victim = !_victimGuid.IsEmpty() ? Global.ObjAccessor.GetUnit(me, _victimGuid) : null; // Search victim if no, not attackable, or out of range, or friendly (possible in case duel end) - if (victim == null || !victim.IsTargetableForAttack() || !me.IsWithinDistInMap(victim, max_range) || me.IsFriendlyTo(victim) || !me.CanSeeOrDetect(victim)) + CanSeeOrDetectExtraArgs canSeeOrDetectExtraArgs = new CanSeeOrDetectExtraArgs() { + IgnorePhaseShift = spellInfo.HasAttribute(SpellAttr6.IgnorePhaseShift), + IncludeHiddenBySpawnTracking = spellInfo.HasAttribute(SpellAttr8.AllowTargetsHiddenBySpawnTracking), + IncludeAnyPrivateObject = spellInfo.HasAttribute(SpellCustomAttributes.CanTargetAnyPrivateObject) + }; + if (victim == null || !victim.IsTargetableForAttack() || !me.IsWithinDistInMap(victim, max_range) || me.IsFriendlyTo(victim) || !me.CanSeeOrDetect(victim, canSeeOrDetectExtraArgs)) { float extraSearchRadius = max_range > 0.0f ? SharedConst.ExtraCellSearchRadius : 0.0f; var u_check = new NearestAttackableUnitInObjectRangeCheck(me, me.GetCharmerOrOwnerOrSelf(), max_range); diff --git a/Source/Game/Entities/Object/WorldObject.cs b/Source/Game/Entities/Object/WorldObject.cs index a6e9e3fc4..64f3b87f2 100644 --- a/Source/Game/Entities/Object/WorldObject.cs +++ b/Source/Game/Entities/Object/WorldObject.cs @@ -1271,18 +1271,18 @@ namespace Game.Entities public SmoothPhasing GetSmoothPhasing() { return _smoothPhasing; } - public bool CanSeeOrDetect(WorldObject obj, bool implicitDetect = false, bool distanceCheck = false, bool checkAlert = false) + public bool CanSeeOrDetect(WorldObject obj, CanSeeOrDetectExtraArgs args = default) { 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; SmoothPhasing smoothPhasing = obj.GetSmoothPhasing(); @@ -1292,17 +1292,21 @@ namespace Game.Entities if (!obj.IsPrivateObject() && !Global.ConditionMgr.IsObjectMeetingVisibilityByObjectIdConditions(obj, this)) return false; - // Spawn tracking Player player = ToPlayer(); - if (player != null) + + // Spawn tracking + if (!args.IncludeHiddenBySpawnTracking) { - SpawnTrackingStateData spawnTrackingStateData = obj.GetSpawnTrackingStateDataForPlayer(player); - if (spawnTrackingStateData != null && !spawnTrackingStateData.Visible) - return false; + if (player != null) + { + SpawnTrackingStateData spawnTrackingStateData = obj.GetSpawnTrackingStateDataForPlayer(player); + if (spawnTrackingStateData != null && !spawnTrackingStateData.Visible) + return false; + } } bool corpseVisibility = false; - if (distanceCheck) + if (args.DistanceCheck) { bool corpseCheck = false; Player thisPlayer = ToPlayer(); @@ -1376,15 +1380,15 @@ namespace Game.Entities if (obj.IsInvisibleDueToDespawn(this)) return false; - if (!CanDetect(obj, implicitDetect, checkAlert)) + if (!CanDetect(obj, args.ImplicitDetection, args.AlertCheck)) return false; return true; } - public virtual bool CanNeverSee(WorldObject obj) + public virtual bool CanNeverSee(WorldObject obj, bool ignorePhaseShift = false) { - return GetMap() != obj.GetMap() || !InSamePhase(obj); + return GetMap() != obj.GetMap() || (!ignorePhaseShift && !InSamePhase(obj)); } public virtual bool CanAlwaysSee(WorldObject obj) { return false; } @@ -2679,11 +2683,16 @@ namespace Game.Entities if (unit != null) { // can't attack invisible - if (bySpell == null || !bySpell.HasAttribute(SpellAttr6.IgnorePhaseShift)) + CanSeeOrDetectExtraArgs canSeeOrDetectExtraArgs = new(); + if (bySpell != null) { - if (!unit.CanSeeOrDetect(target, bySpell != null && bySpell.IsAffectingArea())) - return false; + canSeeOrDetectExtraArgs.ImplicitDetection = bySpell.IsAffectingArea(); + canSeeOrDetectExtraArgs.IgnorePhaseShift = bySpell.HasAttribute(SpellAttr6.IgnorePhaseShift); + canSeeOrDetectExtraArgs.IncludeHiddenBySpawnTracking = bySpell.HasAttribute(SpellAttr8.AllowTargetsHiddenBySpawnTracking); + canSeeOrDetectExtraArgs.IncludeAnyPrivateObject = bySpell.HasAttribute(SpellCustomAttributes.CanTargetAnyPrivateObject); } + if (!unit.CanSeeOrDetect(target, canSeeOrDetectExtraArgs)) + return false; } // can't attack dead @@ -2845,7 +2854,15 @@ namespace Game.Entities } // can't assist invisible - if ((bySpell == null || !bySpell.HasAttribute(SpellAttr6.IgnorePhaseShift)) && !CanSeeOrDetect(target, bySpell != null && bySpell.IsAffectingArea())) + CanSeeOrDetectExtraArgs canSeeOrDetectExtraArgs = new(); + if (bySpell != null) + { + canSeeOrDetectExtraArgs.ImplicitDetection = bySpell.IsAffectingArea(); + canSeeOrDetectExtraArgs.IgnorePhaseShift = bySpell.HasAttribute(SpellAttr6.IgnorePhaseShift); + canSeeOrDetectExtraArgs.IncludeHiddenBySpawnTracking = bySpell.HasAttribute(SpellAttr8.AllowTargetsHiddenBySpawnTracking); + canSeeOrDetectExtraArgs.IncludeAnyPrivateObject = bySpell.HasAttribute(SpellCustomAttributes.CanTargetAnyPrivateObject); + } + if (!CanSeeOrDetect(target, canSeeOrDetectExtraArgs)) return false; // can't assist dead @@ -4332,4 +4349,15 @@ namespace Game.Entities return _changesMask[(int)index]; } } + + public struct CanSeeOrDetectExtraArgs + { + public bool ImplicitDetection; + public bool IgnorePhaseShift; + public bool IncludeHiddenBySpawnTracking; + public bool IncludeAnyPrivateObject; + + public bool DistanceCheck; + public bool AlertCheck; + } } \ No newline at end of file diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 4a7fa6808..be7fac2e1 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -3959,11 +3959,11 @@ namespace Game.Entities return false; } - public override bool CanNeverSee(WorldObject obj) + public override bool CanNeverSee(WorldObject obj, bool ignorePhaseShift = false) { // 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(PlayerLocalFlags.OverrideTransportServerTime) || base.CanNeverSee(obj); + return !HasPlayerLocalFlag(PlayerLocalFlags.OverrideTransportServerTime) || base.CanNeverSee(obj, ignorePhaseShift); } public override bool CanAlwaysSee(WorldObject obj) @@ -6229,7 +6229,7 @@ namespace Game.Entities { if (HaveAtClient(target)) { - if (!CanSeeOrDetect(target, false, true)) + if (!CanSeeOrDetect(target, new CanSeeOrDetectExtraArgs() { DistanceCheck = true })) { BeforeVisibilityDestroy(target, this); @@ -6243,7 +6243,7 @@ namespace Game.Entities } else { - if (CanSeeOrDetect(target, false, true)) + if (CanSeeOrDetect(target, new CanSeeOrDetectExtraArgs() { DistanceCheck = true })) { target.SendUpdateToPlayer(this); m_clientGUIDs.Add(target.GetGUID()); @@ -6259,7 +6259,7 @@ namespace Game.Entities { if (HaveAtClient(target)) { - if (!CanSeeOrDetect(target, false, true)) + if (!CanSeeOrDetect(target, new CanSeeOrDetectExtraArgs() { DistanceCheck = true })) { BeforeVisibilityDestroy(target, this); @@ -6273,7 +6273,7 @@ namespace Game.Entities } else { - if (CanSeeOrDetect(target, false, true)) + if (CanSeeOrDetect(target, new CanSeeOrDetectExtraArgs() { DistanceCheck = true })) { target.BuildCreateUpdateBlockForPlayer(data, this); m_clientGUIDs.Add(target.GetGUID()); diff --git a/Source/Game/Maps/GridNotifiers.cs b/Source/Game/Maps/GridNotifiers.cs index 5c11f3c92..059a46efa 100644 --- a/Source/Game/Maps/GridNotifiers.cs +++ b/Source/Game/Maps/GridNotifiers.cs @@ -32,11 +32,11 @@ namespace Game.Maps if (!c.HasUnitState(UnitState.Sightless)) { - if (c.IsAIEnabled() && c.CanSeeOrDetect(u, false, true)) + if (c.IsAIEnabled() && c.CanSeeOrDetect(u, new CanSeeOrDetectExtraArgs() { DistanceCheck = true })) c.GetAI().MoveInLineOfSight_Safe(u); else { - if (u.IsTypeId(TypeId.Player) && u.HasStealthAura() && c.IsAIEnabled() && c.CanSeeOrDetect(u, false, true, true)) + if (u.IsTypeId(TypeId.Player) && u.HasStealthAura() && c.IsAIEnabled() && c.CanSeeOrDetect(u, new CanSeeOrDetectExtraArgs() { DistanceCheck = true, AlertCheck = true })) c.GetAI().TriggerAlert(u); } } diff --git a/Source/Game/Spells/SpellInfo.cs b/Source/Game/Spells/SpellInfo.cs index 405676158..1a07b105f 100644 --- a/Source/Game/Spells/SpellInfo.cs +++ b/Source/Game/Spells/SpellInfo.cs @@ -995,8 +995,15 @@ namespace Game.Spells if (HasAttribute(SpellAttr1.ExcludeCaster) && caster == target) return SpellCastResult.BadTargets; - // check visibility - ignore invisibility/stealth for implicit (area) targets - if (!HasAttribute(SpellAttr6.IgnorePhaseShift) && !caster.CanSeeOrDetect(target, Implicit)) + // check visibility - Ignore invisibility/stealth for implicit (area) targets + CanSeeOrDetectExtraArgs canSeeOrDetectExtraArgs = new CanSeeOrDetectExtraArgs + { + ImplicitDetection = Implicit, + IgnorePhaseShift = HasAttribute(SpellAttr6.IgnorePhaseShift), + IncludeHiddenBySpawnTracking = HasAttribute(SpellAttr8.AllowTargetsHiddenBySpawnTracking), + IncludeAnyPrivateObject = HasAttribute(SpellCustomAttributes.CanTargetAnyPrivateObject) + }; + if (!caster.CanSeeOrDetect(target, canSeeOrDetectExtraArgs)) return SpellCastResult.BadTargets; Unit unitTarget = target.ToUnit();