diff --git a/Code/client/Games/Skyrim/Magic/EffectItem.cpp b/Code/client/Games/Skyrim/Magic/EffectItem.cpp index e74998c46..0cbb06156 100644 --- a/Code/client/Games/Skyrim/Magic/EffectItem.cpp +++ b/Code/client/Games/Skyrim/Magic/EffectItem.cpp @@ -33,6 +33,11 @@ bool EffectItem::IsVampireLordEffect() const noexcept return pEffectSetting->eArchetype == EffectArchetypes::ArchetypeID::kVampireLord; } +bool EffectItem::IsDetectLifeEffect() const noexcept +{ + return pEffectSetting->eArchetype == EffectArchetypes::ArchetypeID::kDetectLife; +} + bool EffectItem::IsNightVisionEffect() const noexcept { BGSKeyword* pMagicNightEye = Cast(TESForm::GetById(0xad7c6)); diff --git a/Code/client/Games/Skyrim/Magic/EffectItem.h b/Code/client/Games/Skyrim/Magic/EffectItem.h index c4ee9a5d4..9c673828f 100644 --- a/Code/client/Games/Skyrim/Magic/EffectItem.h +++ b/Code/client/Games/Skyrim/Magic/EffectItem.h @@ -20,6 +20,7 @@ struct EffectItem bool IsWerewolfEffect() const noexcept; bool IsVampireLordEffect() const noexcept; bool IsNightVisionEffect() const noexcept; + bool IsDetectLifeEffect() const noexcept; EffectItemData data; uint32_t padC; diff --git a/Code/client/Games/Skyrim/Magic/MagicTarget.cpp b/Code/client/Games/Skyrim/Magic/MagicTarget.cpp index fe6a5ac5b..3151a7ad7 100644 --- a/Code/client/Games/Skyrim/Magic/MagicTarget.cpp +++ b/Code/client/Games/Skyrim/Magic/MagicTarget.cpp @@ -60,6 +60,10 @@ bool MagicTarget::AddTarget(AddTargetData& arData, bool aApplyHealPerkBonus, boo bool MagicTarget::AddTargetData::ShouldSync() { + // Detect life is an area effect on every actor around the caster, syncing it would show them to other players too. + if (pEffectItem->IsDetectLifeEffect()) + return false; + // Constant effect slow time is an ability each client's scripts add and remove on their own player, so a synced // copy would stay on the other players for good. Fire and forget slow time (shouts) still syncs. if (pEffectItem->IsSlowEffect()) @@ -102,6 +106,18 @@ bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& ar if (arData.pEffectItem->IsVampireLordEffect()) pTargetActorEx->GraphDescriptorHash = AnimationGraphDescriptor_VampireLordBehavior::m_key; + if (arData.pEffectItem->IsDetectLifeEffect() && arData.pCaster) + { + const ActorExtension* pCasterExtension = arData.pCaster->GetExtension(); + + // The caster's copy repeats the cast on this client, but only the caster gets to see living targets. + if (pCasterExtension->IsRemotePlayer()) + return false; + + if (pCasterExtension->IsLocalPlayer() && pTargetActorEx->IsRemotePlayer() && World::Get().GetServerSettings().PvpEnabled) + return TiltedPhoques::ThisCall(RealAddTarget, apThis, arData); + } + if (ScopedSpellCastOverride::IsOverriden()) return TiltedPhoques::ThisCall(RealAddTarget, apThis, arData);