Core/Misc: Added new CanSeeOrDetectExtraArgs to CanSeeOrDetect (#30882)
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -1578,18 +1578,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())
|
||||
@@ -1600,13 +1600,14 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool implicitDetect, bo
|
||||
return false;
|
||||
|
||||
// Spawn tracking
|
||||
if (Player const* player = ToPlayer())
|
||||
if (SpawnTrackingStateData const* spawnTrackingStateData = obj->GetSpawnTrackingStateDataForPlayer(player))
|
||||
if (!spawnTrackingStateData->Visible)
|
||||
return false;
|
||||
if (!args.IncludeHiddenBySpawnTracking)
|
||||
if (Player const* player = ToPlayer())
|
||||
if (SpawnTrackingStateData const* spawnTrackingStateData = obj->GetSpawnTrackingStateDataForPlayer(player))
|
||||
if (!spawnTrackingStateData->Visible)
|
||||
return false;
|
||||
|
||||
bool corpseVisibility = false;
|
||||
if (distanceCheck)
|
||||
if (args.DistanceCheck)
|
||||
{
|
||||
bool corpseCheck = false;
|
||||
if (Player const* thisPlayer = ToPlayer())
|
||||
@@ -1674,15 +1675,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
|
||||
@@ -3124,11 +3125,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
|
||||
@@ -3284,7 +3290,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
|
||||
|
||||
@@ -627,6 +627,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:
|
||||
@@ -746,7 +757,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;
|
||||
@@ -956,7 +967,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; }
|
||||
|
||||
@@ -24118,11 +24118,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
|
||||
@@ -24283,7 +24283,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
|
||||
{
|
||||
if (HaveAtClient(target))
|
||||
{
|
||||
if (!CanSeeOrDetect(target, false, true))
|
||||
if (!CanSeeOrDetect(target, { .DistanceCheck = true }))
|
||||
{
|
||||
switch (target->GetTypeId())
|
||||
{
|
||||
@@ -24314,7 +24314,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());
|
||||
@@ -24409,7 +24409,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);
|
||||
|
||||
@@ -24427,7 +24427,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());
|
||||
|
||||
@@ -3230,7 +3230,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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,7 +744,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
|
||||
|
||||
@@ -2239,8 +2239,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();
|
||||
|
||||
@@ -166,6 +166,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);
|
||||
|
||||
Reference in New Issue
Block a user