From 329329ca1cefdde581f64e7f68422fa7bb926b60 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Thu, 9 Apr 2026 10:57:04 +0100 Subject: [PATCH 1/3] network waterlevel to all players, duplicate watersplash with halved volume --- src/game/client/c_baseplayer.cpp | 6 + src/game/client/fx_water.cpp | 189 +++++++++++++++++++++++++++++ src/game/server/neo/neo_player.cpp | 32 +++++ src/game/server/neo/neo_player.h | 1 + src/game/server/player.cpp | 6 + 5 files changed, 234 insertions(+) diff --git a/src/game/client/c_baseplayer.cpp b/src/game/client/c_baseplayer.cpp index dabd3125b..21e5f5170 100644 --- a/src/game/client/c_baseplayer.cpp +++ b/src/game/client/c_baseplayer.cpp @@ -271,7 +271,9 @@ END_RECV_TABLE() RecvPropFloat ( RECVINFO( m_flDeathTime )), +#ifndef NEO RecvPropInt ( RECVINFO( m_nWaterLevel ) ), +#endif // NEO RecvPropFloat ( RECVINFO( m_flLaggedMovementValue )), END_RECV_TABLE() @@ -322,6 +324,10 @@ END_RECV_TABLE() RecvPropString( RECVINFO(m_szLastPlaceName) ), +#ifdef NEO + RecvPropInt ( RECVINFO( m_nWaterLevel ) ), +#endif // NEO + #if defined USES_ECON_ITEMS RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ), #endif diff --git a/src/game/client/fx_water.cpp b/src/game/client/fx_water.cpp index 187360734..f6e1349f4 100644 --- a/src/game/client/fx_water.cpp +++ b/src/game/client/fx_water.cpp @@ -248,6 +248,139 @@ void FX_GunshotSplash( const Vector &origin, const Vector &normal, float scale ) C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep ); } +#ifdef NEO +void FX_PlayerSplash( const Vector &origin, const Vector &normal, float scale ) +{ + VPROF_BUDGET( "FX_PlayerSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); + + if ( cl_show_splashes.GetBool() == false ) + return; + + Vector color; + float luminosity; + + // Get our lighting information + FX_GetSplashLighting( origin + ( normal * scale ), &color, &luminosity ); + + float flScale = scale / 8.0f; + + if ( flScale > 4.0f ) + { + flScale = 4.0f; + } + + // Setup our trail emitter + CSmartPtr sparkEmitter = CTrailParticles::Create( "splash" ); + + if ( !sparkEmitter ) + return; + + sparkEmitter->SetSortOrigin( origin ); + sparkEmitter->m_ParticleCollision.SetGravity( 800.0f ); + sparkEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN ); + sparkEmitter->SetVelocityDampen( 2.0f ); + sparkEmitter->GetBinding().SetBBox( origin - Vector( 32, 32, 32 ), origin + Vector( 32, 32, 32 ) ); + + PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/splash2" ); + + TrailParticle *tParticle; + + Vector offDir; + Vector offset; + float colorRamp; + + //Dump out drops + for ( int i = 0; i < 16; i++ ) + { + offset = origin; + offset[0] += random->RandomFloat( -8.0f, 8.0f ) * flScale; + offset[1] += random->RandomFloat( -8.0f, 8.0f ) * flScale; + + tParticle = (TrailParticle *) sparkEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset ); + + if ( tParticle == NULL ) + break; + + tParticle->m_flLifetime = 0.0f; + tParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); + + offDir = normal + RandomVector( -0.8f, 0.8f ); + + tParticle->m_vecVelocity = offDir * random->RandomFloat( SPLASH_MIN_SPEED * flScale * 3.0f, SPLASH_MAX_SPEED * flScale * 3.0f ); + tParticle->m_vecVelocity[2] += random->RandomFloat( 32.0f, 64.0f ) * flScale; + + tParticle->m_flWidth = random->RandomFloat( 1.0f, 3.0f ); + tParticle->m_flLength = random->RandomFloat( 0.025f, 0.05f ); + + colorRamp = random->RandomFloat( 0.75f, 1.25f ); + + tParticle->m_color.r = MIN( 1.0f, color[0] * colorRamp ) * 255; + tParticle->m_color.g = MIN( 1.0f, color[1] * colorRamp ) * 255; + tParticle->m_color.b = MIN( 1.0f, color[2] * colorRamp ) * 255; + tParticle->m_color.a = luminosity * 255; + } + + // Setup the particle emitter + CSmartPtr pSimple = CSplashParticle::Create( "splish" ); + pSimple->SetSortOrigin( origin ); + pSimple->SetClipHeight( origin.z ); + pSimple->SetParticleCullRadius( scale * 2.0f ); + pSimple->GetBinding().SetBBox( origin - Vector( 32, 32, 32 ), origin + Vector( 32, 32, 32 ) ); + + SimpleParticle *pParticle; + + //Main gout + for ( int i = 0; i < 8; i++ ) + { + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, origin ); + + if ( pParticle == NULL ) + break; + + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = 2.0f; //NOTENOTE: We use a clip plane to realistically control our lifespan + + pParticle->m_vecVelocity.Random( -0.2f, 0.2f ); + pParticle->m_vecVelocity += ( normal * random->RandomFloat( 4.0f, 6.0f ) ); + + VectorNormalize( pParticle->m_vecVelocity ); + + pParticle->m_vecVelocity *= 50 * flScale * (8-i); + + colorRamp = random->RandomFloat( 0.75f, 1.25f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = 24 * flScale * RemapValClamped( i, 7, 0, 1, 0.5f ); + pParticle->m_uchEndSize = MIN( 255, pParticle->m_uchStartSize * 2 ); + + pParticle->m_uchStartAlpha = RemapValClamped( i, 7, 0, 255, 32 ) * luminosity; + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f ); + } + + // Do a ripple + FX_WaterRipple( origin, flScale, &color, 1.5f, luminosity ); + + //Play a sound + CLocalPlayerFilter filter; + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = "Physics.WaterSplash"; + ep.m_flVolume = 0.5f; + ep.m_SoundLevel = SNDLVL_NORM; + ep.m_pOrigin = &origin; + + + C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep ); +} +#endif // NEO + //----------------------------------------------------------------------------- // Purpose: // Input : &origin - @@ -409,6 +542,43 @@ void FX_GunshotSlimeSplash( const Vector &origin, const Vector &normal, float sc C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep ); } +#ifdef NEO +void FX_PlayerSlimeSplash( const Vector &origin, const Vector &normal, float scale ) +{ + if ( cl_show_splashes.GetBool() == false ) + return; + + VPROF_BUDGET( "FX_PlayerSlimeSplash", VPROF_BUDGETGROUP_PARTICLE_RENDERING ); + + QAngle vecAngles; + VectorAngles( normal, vecAngles ); + if ( scale < 2.0f ) + { + DispatchParticleEffect( "slime_splash_01", origin, vecAngles ); + } + else if ( scale < 4.0f ) + { + DispatchParticleEffect( "slime_splash_02", origin, vecAngles ); + } + else + { + DispatchParticleEffect( "slime_splash_03", origin, vecAngles ); + } + + //Play a sound + CLocalPlayerFilter filter; + + EmitSound_t ep; + ep.m_nChannel = CHAN_VOICE; + ep.m_pSoundName = "Physics.WaterSplash"; + ep.m_flVolume = 0.5f; + ep.m_SoundLevel = SNDLVL_NORM; + ep.m_pOrigin = &origin; + + C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, ep ); +} +#endif // NEO + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -430,6 +600,25 @@ void SplashCallback( const CEffectData &data ) DECLARE_CLIENT_EFFECT( "watersplash", SplashCallback ); +#ifdef NEO +void PlayerSplashCallback( const CEffectData &data ) +{ + Vector normal; + + AngleVectors( data.m_vAngles, &normal ); + + if ( data.m_fFlags & FX_WATER_IN_SLIME ) + { + FX_PlayerSlimeSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale ); + } + else + { + FX_PlayerSplash( data.m_vOrigin, Vector(0,0,1), data.m_flScale ); + } +} + +DECLARE_CLIENT_EFFECT( "playersplash", PlayerSplashCallback ); +#endif // NEO //----------------------------------------------------------------------------- // Purpose: diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index 3d6c42f6d..e286af92e 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -41,6 +41,9 @@ #include "nav_mesh.h" #include "neo_spawn_manager.h" +#include "effect_dispatch_data.h" +#include "te_effect_dispatch.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -4055,6 +4058,35 @@ const char *CNEO_Player::GetOverrideStepSound(const char *pBaseStepSound) return BaseClass::GetOverrideStepSound(pBaseStepSound); } +void CNEO_Player::Splash() +{ + CEffectData data; + data.m_fFlags = 0; + data.m_vOrigin = GetAbsOrigin(); + data.m_vNormal = Vector(0,0,1); + data.m_vAngles = QAngle( 0, 0, 0 ); + + if ( GetWaterType() & CONTENTS_SLIME ) + { + data.m_fFlags |= FX_WATER_IN_SLIME; + } + + CPASFilter filter( data.m_vOrigin ); + filter.SetIgnorePredictionCull(true); // NEO TODO (Adam) predict this effect for the local player client side instead + + float flSpeed = GetAbsVelocity().Length(); + if ( flSpeed < 300 ) + { + data.m_flScale = random->RandomFloat( 10, 12 ); + DispatchEffect( "waterripple", data, filter ); + } + else + { + data.m_flScale = random->RandomFloat( 6, 8 ); + DispatchEffect( "playersplash", data, filter ); + } +} + // Start spectator takeover of player related code: ConVar sv_neo_spec_replace_player_loadout_enable("sv_neo_spec_replace_player_loadout_enable", "0", FCVAR_NONE, "Allow loadout change after spectator takeover.", true, 0, true, 1); ConVar sv_neo_spec_replace_player_bot_enable("sv_neo_spec_replace_player_bot_enable", "1", FCVAR_NONE, "Allow spectators to take over bots.", true, 0, true, 1); diff --git a/src/game/server/neo/neo_player.h b/src/game/server/neo/neo_player.h index ba3252822..192f6b286 100644 --- a/src/game/server/neo/neo_player.h +++ b/src/game/server/neo/neo_player.h @@ -86,6 +86,7 @@ class CNEO_Player : public CHL2MP_Player virtual void PickupObject(CBaseEntity *pObject, bool bLimitMassAndSize) OVERRIDE; virtual void PlayStepSound(Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force) OVERRIDE; const char* GetOverrideStepSound(const char* pBaseStepSound) override; + virtual void Splash() override; virtual void Weapon_Drop(CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL) OVERRIDE; void Weapon_DropOnDeath(CNEOBaseCombatWeapon *pWeapon, Vector damageForce); void Weapon_DropAllOnDeath(const CTakeDamageInfo &info); diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 49dff9b3e..596f300be 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -8496,7 +8496,9 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi SendPropFloat ( SENDINFO( m_flDeathTime ), 0, SPROP_NOSCALE ), +#ifndef NEO SendPropInt ( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ), +#endif // NEO SendPropFloat ( SENDINFO( m_flLaggedMovementValue ), 0, SPROP_NOSCALE ), END_SEND_TABLE() @@ -8536,6 +8538,10 @@ void SendProxy_CropFlagsToPlayerFlagBitsLength( const SendProp *pProp, const voi SendPropArray ( SendPropEHandle( SENDINFO_ARRAY( m_hViewModel ) ), m_hViewModel ), SendPropString (SENDINFO(m_szLastPlaceName) ), +#ifdef NEO + SendPropInt ( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ), +#endif // NEO + #if defined USES_ECON_ITEMS SendPropUtlVector( SENDINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, SendPropEHandle( NULL, 0 ) ), #endif // USES_ECON_ITEMS From cbee133f939a269a7a8b32321ff3b4f89ccfead8 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Thu, 9 Apr 2026 11:17:33 +0100 Subject: [PATCH 2/3] predict water splashes for the local player --- src/game/client/c_baseentity.h | 4 +++ src/game/client/neo/c_neo_player.h | 1 + src/game/server/neo/neo_player.cpp | 32 ------------------- src/game/shared/gamemovement.cpp | 4 +++ src/game/shared/neo/neo_player_shared.cpp | 39 +++++++++++++++++++++++ 5 files changed, 48 insertions(+), 32 deletions(-) diff --git a/src/game/client/c_baseentity.h b/src/game/client/c_baseentity.h index 2200f6db1..b5c64a575 100644 --- a/src/game/client/c_baseentity.h +++ b/src/game/client/c_baseentity.h @@ -555,6 +555,10 @@ class C_BaseEntity : public IClientEntity MoveCollide_t GetMoveCollide( void ) const; virtual SolidType_t GetSolid( void ) const; +#ifdef NEO + virtual void Splash() {} +#endif // NEO + virtual int GetSolidFlags( void ) const; bool IsSolidFlagSet( int flagMask ) const; void SetSolidFlags( int nFlags ); diff --git a/src/game/client/neo/c_neo_player.h b/src/game/client/neo/c_neo_player.h index 590769b11..b613f5ebf 100644 --- a/src/game/client/neo/c_neo_player.h +++ b/src/game/client/neo/c_neo_player.h @@ -71,6 +71,7 @@ class C_NEO_Player : public C_HL2MP_Player virtual void PostDataUpdate( DataUpdateType_t updateType ); virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ); const char* GetOverrideStepSound(const char* pBaseStepSound) override; + virtual void Splash() override; virtual void DoImpactEffect( trace_t &tr, int nDamageType ); IRagdoll* GetRepresentativeRagdoll() const; virtual void CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ); diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index e286af92e..3d6c42f6d 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -41,9 +41,6 @@ #include "nav_mesh.h" #include "neo_spawn_manager.h" -#include "effect_dispatch_data.h" -#include "te_effect_dispatch.h" - // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -4058,35 +4055,6 @@ const char *CNEO_Player::GetOverrideStepSound(const char *pBaseStepSound) return BaseClass::GetOverrideStepSound(pBaseStepSound); } -void CNEO_Player::Splash() -{ - CEffectData data; - data.m_fFlags = 0; - data.m_vOrigin = GetAbsOrigin(); - data.m_vNormal = Vector(0,0,1); - data.m_vAngles = QAngle( 0, 0, 0 ); - - if ( GetWaterType() & CONTENTS_SLIME ) - { - data.m_fFlags |= FX_WATER_IN_SLIME; - } - - CPASFilter filter( data.m_vOrigin ); - filter.SetIgnorePredictionCull(true); // NEO TODO (Adam) predict this effect for the local player client side instead - - float flSpeed = GetAbsVelocity().Length(); - if ( flSpeed < 300 ) - { - data.m_flScale = random->RandomFloat( 10, 12 ); - DispatchEffect( "waterripple", data, filter ); - } - else - { - data.m_flScale = random->RandomFloat( 6, 8 ); - DispatchEffect( "playersplash", data, filter ); - } -} - // Start spectator takeover of player related code: ConVar sv_neo_spec_replace_player_loadout_enable("sv_neo_spec_replace_player_loadout_enable", "0", FCVAR_NONE, "Allow loadout change after spectator takeover.", true, 0, true, 1); ConVar sv_neo_spec_replace_player_bot_enable("sv_neo_spec_replace_player_bot_enable", "1", FCVAR_NONE, "Allow spectators to take over bots.", true, 0, true, 1); diff --git a/src/game/shared/gamemovement.cpp b/src/game/shared/gamemovement.cpp index e8ef48a39..979aec66e 100644 --- a/src/game/shared/gamemovement.cpp +++ b/src/game/shared/gamemovement.cpp @@ -2251,9 +2251,13 @@ void CGameMovement::FullWalkMove( ) ( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) ) { PlaySwimSound(); +#ifdef NEO + player->Splash(); +#else #if !defined( CLIENT_DLL ) player->Splash(); #endif +#endif // NEO } } diff --git a/src/game/shared/neo/neo_player_shared.cpp b/src/game/shared/neo/neo_player_shared.cpp index 7de2bb4de..066bf779e 100644 --- a/src/game/shared/neo/neo_player_shared.cpp +++ b/src/game/shared/neo/neo_player_shared.cpp @@ -29,6 +29,13 @@ #include "weapon_neobasecombatweapon.h" +#include "effect_dispatch_data.h" +#ifdef GAME_DLL +#include "te_effect_dispatch.h" +#else +#include "c_te_effect_dispatch.h" +#endif // GAME_DLL + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -393,3 +400,35 @@ void CNEO_Player::CheckAimButtons() Weapon_SetZoom(false); } } + +void CNEO_Player::Splash() +{ + CEffectData data; + data.m_fFlags = 0; + data.m_vOrigin = GetAbsOrigin(); + data.m_vNormal = Vector(0,0,1); + data.m_vAngles = QAngle( 0, 0, 0 ); + + if ( GetWaterType() & CONTENTS_SLIME ) + { + data.m_fFlags |= FX_WATER_IN_SLIME; + } + +#ifdef GAME_DLL + CPASFilter filter( data.m_vOrigin ); +#else + CLocalPlayerFilter filter; +#endif // GAME_DLL + + float flSpeed = GetAbsVelocity().Length(); + if ( flSpeed < 300 ) + { + data.m_flScale = random->RandomFloat( 10, 12 ); + DispatchEffect( "waterripple", data, filter ); + } + else + { + data.m_flScale = random->RandomFloat( 6, 8 ); + DispatchEffect( "playersplash", data, filter ); + } +} From ecd41af91980f28ff8c38f68b2ec0c0cc4ba0cac Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Sun, 12 Apr 2026 11:24:09 +0100 Subject: [PATCH 3/3] lengthsqr --- src/game/shared/neo/neo_player_shared.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/game/shared/neo/neo_player_shared.cpp b/src/game/shared/neo/neo_player_shared.cpp index 066bf779e..c723b012e 100644 --- a/src/game/shared/neo/neo_player_shared.cpp +++ b/src/game/shared/neo/neo_player_shared.cpp @@ -420,8 +420,9 @@ void CNEO_Player::Splash() CLocalPlayerFilter filter; #endif // GAME_DLL - float flSpeed = GetAbsVelocity().Length(); - if ( flSpeed < 300 ) + float flSpeedSqr = GetAbsVelocity().LengthSqr(); + constexpr int SPLASH_SPEED_THRESHOLD = 300; + if ( flSpeedSqr < SPLASH_SPEED_THRESHOLD * SPLASH_SPEED_THRESHOLD ) { data.m_flScale = random->RandomFloat( 10, 12 ); DispatchEffect( "waterripple", data, filter );