From 0912aa3bd20ee5c04b941e4eb8084944a915738e Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Sun, 17 May 2026 21:46:36 +0100 Subject: [PATCH 1/4] init --- src/game/client/neo/c_neo_player.cpp | 18 +++++++++++++++--- src/game/client/neo/c_neo_player.h | 2 +- .../neo/weapons/weapon_neobasecombatweapon.cpp | 4 ++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index c473a99797..7e95858fc1 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -688,12 +688,12 @@ int C_NEO_Player::DrawModel(int flags) bool inThermalVision = pTargetPlayer ? (pTargetPlayer->IsInVision() && pTargetPlayer->GetClass() == NEO_CLASS_SUPPORT) : false; int ret = 0; - if (!IsCloaked() || inThermalVision) + if (!m_bInThermOpticCamo || inThermalVision) { ret |= BaseClass::DrawModel(flags); } - if (IsCloaked() && !inThermalVision) + if (m_bInThermOpticCamo && !inThermalVision) { IMaterial* pass = materials->FindMaterial("models/player/toc", TEXTURE_GROUP_CLIENT_EFFECTS); modelrender->ForcedMaterialOverride(pass); @@ -701,7 +701,7 @@ int C_NEO_Player::DrawModel(int flags) modelrender->ForcedMaterialOverride(nullptr); } - else if (inThermalVision && !IsCloaked()) + else if (inThermalVision && !m_bInThermOpticCamo) { IMaterial* pass = materials->FindMaterial(NEO_THERMAL_MODEL_MATERIAL, TEXTURE_GROUP_MODEL); modelrender->ForcedMaterialOverride(pass); @@ -749,6 +749,18 @@ void C_NEO_Player::AddPoints(int score, bool bAllowNegativeScore, bool bIgnorePl //pl.frags = m_iFrags; NEO TODO (Adam) Is this actually used anywhere? should we include a xp field in CPlayerState? } +bool C_NEO_Player::IsCloaked() const +{ + auto pTargetPlayer = C_NEO_Player::GetVisionTargetNEOPlayer(); + if (!pTargetPlayer) + { + return m_bInThermOpticCamo; + } + + bool inThermalVision = pTargetPlayer->IsInVision() && pTargetPlayer->GetClass() == NEO_CLASS_SUPPORT; + return m_bInThermOpticCamo && !inThermalVision; +} + ShadowType_t C_NEO_Player::ShadowCastType( void ) { if (IsCloaked()) diff --git a/src/game/client/neo/c_neo_player.h b/src/game/client/neo/c_neo_player.h index b613f5ebf8..761c0fd2fe 100644 --- a/src/game/client/neo/c_neo_player.h +++ b/src/game/client/neo/c_neo_player.h @@ -162,7 +162,7 @@ class C_NEO_Player : public C_HL2MP_Player C_NEOPredictedViewModel *GetNEOViewModel() { return static_cast(GetViewModel()); } - bool IsCloaked() const { return m_bInThermOpticCamo; } + bool IsCloaked() const; float GetCloakFactor() const { return m_flTocFactor; } bool IsAirborne() const { return (!(GetFlags() & FL_ONGROUND)); } bool IsInVision() const { return m_bInVision; } diff --git a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp index d9783b1b2c..2f2f99ed75 100644 --- a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp +++ b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp @@ -1293,7 +1293,7 @@ int CNEOBaseCombatWeapon::DrawModel(int flags) bool inThermalVision = pTargetPlayer->IsInVision() && pTargetPlayer->GetClass() == NEO_CLASS_SUPPORT; int ret = 0; - if (inThermalVision && (!pOwner || (pOwner && !pOwner->IsCloaked()))) + if (inThermalVision && (!pOwner || (pOwner && !pOwner->m_bInThermOpticCamo))) { IMaterial* pass = materials->FindMaterial("dev/thermal_weapon_model", TEXTURE_GROUP_MODEL); modelrender->ForcedMaterialOverride(pass); @@ -1302,7 +1302,7 @@ int CNEOBaseCombatWeapon::DrawModel(int flags) return ret; } - if ((pOwner && pOwner->IsCloaked()) && !inThermalVision) + if ((pOwner && pOwner->m_bInThermOpticCamo) && !inThermalVision) { IMaterial* pass = materials->FindMaterial("models/player/toc", TEXTURE_GROUP_CLIENT_EFFECTS); modelrender->ForcedMaterialOverride(pass); From b047e88ed6d43faafb487f524ff35fa8091179a0 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Sun, 17 May 2026 21:52:56 +0100 Subject: [PATCH 2/4] don't cast shadows when cloaked in thermals? --- src/game/client/neo/c_neo_player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 7e95858fc1..8a6d513084 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -763,7 +763,7 @@ bool C_NEO_Player::IsCloaked() const ShadowType_t C_NEO_Player::ShadowCastType( void ) { - if (IsCloaked()) + if (m_bInThermOpticCamo) { return SHADOWS_NONE; } From e3ac8849d1c1e743a5220ae33ef9edcf9d4207d6 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Thu, 4 Jun 2026 10:24:32 +0100 Subject: [PATCH 3/4] cleanup --- src/game/client/neo/c_neo_player.cpp | 21 +++++++++++-------- src/game/client/neo/c_neo_player.h | 3 ++- src/game/client/neo/neo_mp3player.cpp | 3 ++- src/game/server/neo/neo_player.cpp | 9 ++++++++ .../shared/neo/neo_predicted_viewmodel.cpp | 10 ++++----- .../weapons/weapon_neobasecombatweapon.cpp | 14 ++++++------- 6 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 8a6d513084..5db155302b 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -688,12 +688,12 @@ int C_NEO_Player::DrawModel(int flags) bool inThermalVision = pTargetPlayer ? (pTargetPlayer->IsInVision() && pTargetPlayer->GetClass() == NEO_CLASS_SUPPORT) : false; int ret = 0; - if (!m_bInThermOpticCamo || inThermalVision) + if (!IsCloaked() || inThermalVision) { ret |= BaseClass::DrawModel(flags); } - if (m_bInThermOpticCamo && !inThermalVision) + if (IsCloaked() && !inThermalVision) { IMaterial* pass = materials->FindMaterial("models/player/toc", TEXTURE_GROUP_CLIENT_EFFECTS); modelrender->ForcedMaterialOverride(pass); @@ -701,7 +701,7 @@ int C_NEO_Player::DrawModel(int flags) modelrender->ForcedMaterialOverride(nullptr); } - else if (inThermalVision && !m_bInThermOpticCamo) + else if (inThermalVision && !IsCloaked()) { IMaterial* pass = materials->FindMaterial(NEO_THERMAL_MODEL_MATERIAL, TEXTURE_GROUP_MODEL); modelrender->ForcedMaterialOverride(pass); @@ -749,21 +749,24 @@ void C_NEO_Player::AddPoints(int score, bool bAllowNegativeScore, bool bIgnorePl //pl.frags = m_iFrags; NEO TODO (Adam) Is this actually used anywhere? should we include a xp field in CPlayerState? } -bool C_NEO_Player::IsCloaked() const +bool C_NEO_Player::IsDrawnTransparent() const { auto pTargetPlayer = C_NEO_Player::GetVisionTargetNEOPlayer(); if (!pTargetPlayer) { - return m_bInThermOpticCamo; + return IsCloaked(); } bool inThermalVision = pTargetPlayer->IsInVision() && pTargetPlayer->GetClass() == NEO_CLASS_SUPPORT; - return m_bInThermOpticCamo && !inThermalVision; + return IsCloaked() && !inThermalVision; } ShadowType_t C_NEO_Player::ShadowCastType( void ) { - if (m_bInThermOpticCamo) + // NEO TODO (Adam) should cloaked players cast shadows in thermals? If they are drawn opaque, it follows that cloaked players block light on a + // spectrum that thermals can see, so light in that same spectrum would be absent where the shadow would be, hence the shadow should be drawn + // if so, replace IsCloaked() with IsDrawnTransparent() + if (IsCloaked()) { return SHADOWS_NONE; } @@ -782,12 +785,12 @@ const QAngle& C_NEO_Player::GetRenderAngles() RenderGroup_t C_NEO_Player::GetRenderGroup() { - return IsCloaked() ? RENDER_GROUP_TRANSLUCENT_ENTITY : RENDER_GROUP_OPAQUE_ENTITY; + return IsDrawnTransparent() ? RENDER_GROUP_TRANSLUCENT_ENTITY : RENDER_GROUP_OPAQUE_ENTITY; } bool C_NEO_Player::UsesPowerOfTwoFrameBufferTexture() { - return IsCloaked(); + return IsDrawnTransparent(); } bool C_NEO_Player::ShouldDraw( void ) diff --git a/src/game/client/neo/c_neo_player.h b/src/game/client/neo/c_neo_player.h index 761c0fd2fe..08d8f2af7a 100644 --- a/src/game/client/neo/c_neo_player.h +++ b/src/game/client/neo/c_neo_player.h @@ -162,7 +162,8 @@ class C_NEO_Player : public C_HL2MP_Player C_NEOPredictedViewModel *GetNEOViewModel() { return static_cast(GetViewModel()); } - bool IsCloaked() const; + inline bool IsCloaked() const { return m_bInThermOpticCamo; } + bool IsDrawnTransparent() const; float GetCloakFactor() const { return m_flTocFactor; } bool IsAirborne() const { return (!(GetFlags() & FL_ONGROUND)); } bool IsInVision() const { return m_bInVision; } diff --git a/src/game/client/neo/neo_mp3player.cpp b/src/game/client/neo/neo_mp3player.cpp index e9fb60ee4a..b6aeeaa96a 100644 --- a/src/game/client/neo/neo_mp3player.cpp +++ b/src/game/client/neo/neo_mp3player.cpp @@ -157,7 +157,8 @@ class CNeoMP3Thread : public CWorkerThread } float flVol = - (cvr_snd_mute_losefocus.GetBool() && false == engine->IsActiveApp()) + (cvr_snd_mute_losefocus.GetBool() && + false == engine->IsActiveApp()) ? 0.0f : (cl_neo_radio_volume_separate_ingame.GetBool() && IsInGame()) ? cl_neo_radio_volume_ingame.GetFloat() diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 3d6c42f6d4..b3431dffa1 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -231,6 +231,9 @@ ConVar sv_neo_bot_cloak_detection_bonus_lighting("sv_neo_bot_cloak_detection_bon "Bot cloak detection bonus for target being in a well lit area (scaled by light intensity ratio 0.0-1.0)", true, 0, true, 100); +ConVar sv_neo_infinite_cloak("sv_neo_infinite_cloak", "0", FCVAR_CHEAT | FCVAR_REPLICATED, "Don't drain cloak", true, 0, true, 1); + + void CNEO_Player::RequestSetClass(int newClass) { if (newClass < 0 || newClass >= NEO_CLASS_ENUM_COUNT) @@ -3702,6 +3705,12 @@ void CNEO_Player::CloakPower_Update(void) bool CNEO_Player::CloakPower_Drain(float flPower) { + // NEO TODO (Adam) predict client side + if (sv_neo_infinite_cloak.GetBool()) + { + return true; + } + m_HL2Local.m_cloakPower -= flPower; if (m_HL2Local.m_cloakPower < 0.0) diff --git a/src/game/shared/neo/neo_predicted_viewmodel.cpp b/src/game/shared/neo/neo_predicted_viewmodel.cpp index 06e7ed4cf8..fd2ff5bed0 100644 --- a/src/game/shared/neo/neo_predicted_viewmodel.cpp +++ b/src/game/shared/neo/neo_predicted_viewmodel.cpp @@ -626,10 +626,9 @@ void CNEOPredictedViewModel::ProcessMuzzleFlashEvent() RenderGroup_t CNEOPredictedViewModel::GetRenderGroup() { - auto pPlayer = static_cast(GetOwner()); - if (pPlayer) + if (auto pPlayer = static_cast(GetOwner())) { - return pPlayer->IsCloaked() ? RENDER_GROUP_VIEW_MODEL_TRANSLUCENT : RENDER_GROUP_VIEW_MODEL_OPAQUE; + return pPlayer->IsDrawnTransparent() ? RENDER_GROUP_VIEW_MODEL_TRANSLUCENT : RENDER_GROUP_VIEW_MODEL_OPAQUE; } return BaseClass::GetRenderGroup(); @@ -637,10 +636,9 @@ RenderGroup_t CNEOPredictedViewModel::GetRenderGroup() bool CNEOPredictedViewModel::UsesPowerOfTwoFrameBufferTexture() { - auto pPlayer = static_cast(GetOwner()); - if (pPlayer) + if (auto pPlayer = static_cast(GetOwner())) { - return pPlayer->IsCloaked() ? true : false; + return pPlayer->IsDrawnTransparent() ? true : false; } return BaseClass::UsesPowerOfTwoFrameBufferTexture(); diff --git a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp index 2f2f99ed75..7e9666f7f7 100644 --- a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp +++ b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp @@ -1293,7 +1293,7 @@ int CNEOBaseCombatWeapon::DrawModel(int flags) bool inThermalVision = pTargetPlayer->IsInVision() && pTargetPlayer->GetClass() == NEO_CLASS_SUPPORT; int ret = 0; - if (inThermalVision && (!pOwner || (pOwner && !pOwner->m_bInThermOpticCamo))) + if (inThermalVision && (!pOwner || (pOwner && !pOwner->IsCloaked()))) { IMaterial* pass = materials->FindMaterial("dev/thermal_weapon_model", TEXTURE_GROUP_MODEL); modelrender->ForcedMaterialOverride(pass); @@ -1302,7 +1302,7 @@ int CNEOBaseCombatWeapon::DrawModel(int flags) return ret; } - if ((pOwner && pOwner->m_bInThermOpticCamo) && !inThermalVision) + if ((pOwner && pOwner->IsCloaked()) && !inThermalVision) { IMaterial* pass = materials->FindMaterial("models/player/toc", TEXTURE_GROUP_CLIENT_EFFECTS); modelrender->ForcedMaterialOverride(pass); @@ -1318,10 +1318,9 @@ int CNEOBaseCombatWeapon::DrawModel(int flags) RenderGroup_t CNEOBaseCombatWeapon::GetRenderGroup() { - auto pPlayer = static_cast(GetOwner()); - if (pPlayer) + if (auto pPlayer = static_cast(GetOwner())) { - return pPlayer->IsCloaked() ? RENDER_GROUP_TRANSLUCENT_ENTITY : RENDER_GROUP_OPAQUE_ENTITY; + return pPlayer->IsDrawnTransparent() ? RENDER_GROUP_TRANSLUCENT_ENTITY : RENDER_GROUP_OPAQUE_ENTITY; } return BaseClass::GetRenderGroup(); @@ -1329,10 +1328,9 @@ RenderGroup_t CNEOBaseCombatWeapon::GetRenderGroup() bool CNEOBaseCombatWeapon::UsesPowerOfTwoFrameBufferTexture() { - auto pPlayer = static_cast(GetOwner()); - if (pPlayer) + if (auto pPlayer = static_cast(GetOwner())) { - return pPlayer->IsCloaked(); + return pPlayer->IsDrawnTransparent(); } return BaseClass::UsesPowerOfTwoFrameBufferTexture(); From 2a40fb2958ba1c6fe0280c9ed2c326fa0b445222 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Thu, 4 Jun 2026 11:39:45 +0100 Subject: [PATCH 4/4] undo that --- src/game/client/neo/neo_mp3player.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/game/client/neo/neo_mp3player.cpp b/src/game/client/neo/neo_mp3player.cpp index b6aeeaa96a..e9fb60ee4a 100644 --- a/src/game/client/neo/neo_mp3player.cpp +++ b/src/game/client/neo/neo_mp3player.cpp @@ -157,8 +157,7 @@ class CNeoMP3Thread : public CWorkerThread } float flVol = - (cvr_snd_mute_losefocus.GetBool() && - false == engine->IsActiveApp()) + (cvr_snd_mute_losefocus.GetBool() && false == engine->IsActiveApp()) ? 0.0f : (cl_neo_radio_volume_separate_ingame.GetBool() && IsInGame()) ? cl_neo_radio_volume_ingame.GetFloat()