From 94060e8c19e88a08f43160432e9a5f3d5361e6d1 Mon Sep 17 00:00:00 2001 From: Kamay Xutax Date: Mon, 15 Jul 2024 21:10:58 +0200 Subject: [PATCH] Simplified bullet trace --- game/client/c_baseplayer.cpp | 38 ++---- game/client/c_baseplayer.h | 4 +- game/client/cstrike/c_cs_player.h | 10 +- game/client/prediction.cpp | 6 +- game/server/cstrike/cs_eventlog.cpp | 31 ++++- game/server/cstrike/cs_player.h | 4 +- game/server/player.cpp | 8 +- game/server/player.h | 2 - game/server/player_command.cpp | 6 +- game/shared/cstrike/cs_player_shared.cpp | 145 ++++++++++++++--------- 10 files changed, 147 insertions(+), 107 deletions(-) diff --git a/game/client/c_baseplayer.cpp b/game/client/c_baseplayer.cpp index 3bc3942822..a22d300df2 100644 --- a/game/client/c_baseplayer.cpp +++ b/game/client/c_baseplayer.cpp @@ -247,9 +247,7 @@ END_RECV_TABLE() RecvPropInt ( RECVINFO( m_nWaterLevel ) ), RecvPropFloat ( RECVINFO( m_flLaggedMovementValue )), - RecvPropArray3 ( RECVINFO_ARRAY(m_vecBulletPositions), RecvPropVector( RECVINFO(m_vecBulletPositions[0])) ), - RecvPropInt(RECVINFO(m_iBulletPositionCount)), - END_RECV_TABLE() +END_RECV_TABLE() // -------------------------------------------------------------------------------- // @@ -416,8 +414,9 @@ C_BasePlayer::C_BasePlayer() : m_iv_vecViewOffset( "C_BasePlayer::m_iv_vecViewOf m_pFlashlight = NULL; - m_pCurrentVguiScreen = NULL; - m_pCurrentCommand = NULL; + m_pCurrentVguiScreen = NULL; + static CUserCmd nullcmd; + m_pCurrentCommand = &nullcmd; m_flPredictionErrorTime = -100; m_StuckLast = 0; @@ -2132,34 +2131,13 @@ void C_BasePlayer::Simulate() ResetLatched(); } - if (m_nTickBaseFireBullet <= m_nTickBase && m_nTickBaseFireBullet != -1) + bool shouldShowFireBulletHitbox = m_nTickBaseFireBullet <= m_nTickBase && m_nTickBaseFireBullet != -1; + + if (shouldShowFireBulletHitbox) { DrawServerHitboxes(60.0f, true); - static ConVarRef cl_showimpacts("cl_showimpacts"); - - if (cl_showimpacts.GetInt() == 1 || cl_showimpacts.GetInt() == 3) - { - extern float g_bulletDiameter; - for (int i = 0; i < m_iBulletPositionCount; i++) - { - debugoverlay->AddBoxOverlay(m_vecBulletPositions[i], - Vector(-g_bulletDiameter, - -g_bulletDiameter, - -g_bulletDiameter), - Vector(g_bulletDiameter, - g_bulletDiameter, - g_bulletDiameter), - QAngle(0, 0, 0), - 0, - 0, - 255, - 127, - 60); - } - } - m_nTickBaseFireBullet = -1; - } + } } //----------------------------------------------------------------------------- diff --git a/game/client/c_baseplayer.h b/game/client/c_baseplayer.h index 12ebed8cdf..b9933d11c9 100644 --- a/game/client/c_baseplayer.h +++ b/game/client/c_baseplayer.h @@ -8,6 +8,7 @@ #ifndef C_BASEPLAYER_H #define C_BASEPLAYER_H + #ifdef _WIN32 #pragma once #endif @@ -434,6 +435,7 @@ public: int m_nButtons; + CUserCmd m_LastCmd; CUserCmd *m_pCurrentCommand; // Movement constraints @@ -638,8 +640,6 @@ public: void SetOldPlayerZ( float flOld ) { m_flOldPlayerZ = flOld; } int m_nTickBaseFireBullet; - Vector m_vecBulletPositions[MAX_PLAYER_BULLET_POSITIONS]; - int m_iBulletPositionCount; }; EXTERN_RECV_TABLE(DT_BasePlayer); diff --git a/game/client/cstrike/c_cs_player.h b/game/client/cstrike/c_cs_player.h index b001e832ce..6a820d2785 100644 --- a/game/client/cstrike/c_cs_player.h +++ b/game/client/cstrike/c_cs_player.h @@ -168,10 +168,12 @@ public: public: virtual float GetPlayerMaxSpeed(); - void GetBulletTypeParameters( - int iBulletType, - float &fPenetrationPower, - float &flPenetrationDistance ); + float GetBulletDiameter(int iBulletType); + void GetBulletTypeParameters( + int iBulletType, + float &fPenetrationPower, + float &flPenetrationDistance, + float &flBulletDiameter ); void FireBullet( Vector vecSrc, diff --git a/game/client/prediction.cpp b/game/client/prediction.cpp index 6245421217..0be712a209 100644 --- a/game/client/prediction.cpp +++ b/game/client/prediction.cpp @@ -763,9 +763,9 @@ void CPrediction::FinishCommand( C_BasePlayer *player ) #if !defined( NO_ENTITY_PREDICTION ) VPROF( "CPrediction::FinishCommand" ); - player->m_pCurrentCommand = NULL; - C_BaseEntity::SetPredictionRandomSeed( NULL ); - C_BaseEntity::SetPredictionPlayer( NULL ); + // player->m_pCurrentCommand = NULL; + //C_BaseEntity::SetPredictionRandomSeed( NULL ); + //C_BaseEntity::SetPredictionPlayer( NULL ); #endif } diff --git a/game/server/cstrike/cs_eventlog.cpp b/game/server/cstrike/cs_eventlog.cpp index 095f6f2e7c..d1a90a1c3d 100644 --- a/game/server/cstrike/cs_eventlog.cpp +++ b/game/server/cstrike/cs_eventlog.cpp @@ -50,6 +50,7 @@ public: ListenForGameEvent( "hostage_killed" ); ListenForGameEvent( "hostage_follows" ); ListenForGameEvent( "player_hurt" ); + ListenForGameEvent( "bullet_impact" ); return true; } @@ -299,7 +300,35 @@ protected: ); return true; } - + else if ( !Q_strncmp( eventName, "bullet_impact", Q_strlen("bullet_impact") ) ) + { + static ConVarRef cl_showimpacts("cl_showimpacts"); + + if (cl_showimpacts.GetInt() == 1 + || cl_showimpacts.GetInt() == 3) + { + auto x = event->GetFloat("x"); + auto y = event->GetFloat("y"); + auto z = event->GetFloat("z"); + auto flBulletDiameter = event->GetFloat("diameter"); + + debugoverlay->AddBoxOverlay(Vector(x, y, z), + Vector(-flBulletDiameter, + -flBulletDiameter, + -flBulletDiameter), + Vector(flBulletDiameter, + flBulletDiameter, + flBulletDiameter), + QAngle(0, 0, 0), + 0, + 0, + 255, + 127, + 60.0f); + } + + return true; + } // unused events: //hostage_hurt //bomb_exploded diff --git a/game/server/cstrike/cs_player.h b/game/server/cstrike/cs_player.h index d889aa6fd5..50840370c9 100644 --- a/game/server/cstrike/cs_player.h +++ b/game/server/cstrike/cs_player.h @@ -317,10 +317,12 @@ public: float lateral_max, int direction_change ); + float GetBulletDiameter(int iBulletType); void GetBulletTypeParameters( int iBulletType, float &fPenetrationPower, - float &flPenetrationDistance ); + float &flPenetrationDistance, + float &flBulletDiameter ); // Returns true if the player is allowed to move. bool CanMove() const; diff --git a/game/server/player.cpp b/game/server/player.cpp index a85d06f43f..049b001e09 100644 --- a/game/server/player.cpp +++ b/game/server/player.cpp @@ -583,8 +583,9 @@ CBasePlayer::CBasePlayer( ) m_bitsDamageType = 0; m_bForceOrigin = false; - m_hVehicle = NULL; - m_pCurrentCommand = NULL; + m_hVehicle = NULL; + static CUserCmd nullcmd; + m_pCurrentCommand = &nullcmd; // Setup our default FOV m_iDefaultFOV = g_pGameRules->DefaultFOV(); @@ -634,7 +635,6 @@ CBasePlayer::CBasePlayer( ) m_flLastUserCommandTime = 0.f; m_flMovementTimeForUserCmdProcessingRemaining = 0.0f; - m_iBulletPositionCount.Set(0); } CBasePlayer::~CBasePlayer( ) @@ -7970,8 +7970,6 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) SendPropInt ( SENDINFO( m_nWaterLevel ), 2, SPROP_UNSIGNED ), SendPropFloat ( SENDINFO( m_flLaggedMovementValue ), 0, SPROP_NOSCALE ), - SendPropArray3( SENDINFO_ARRAY3(m_vecBulletPositions), SendPropVector(SENDINFO_ARRAY(m_vecBulletPositions))), - SendPropInt(SENDINFO(m_iBulletPositionCount)), END_SEND_TABLE() diff --git a/game/server/player.h b/game/server/player.h index ad00d1de41..407a59e001 100644 --- a/game/server/player.h +++ b/game/server/player.h @@ -1210,8 +1210,6 @@ private: public: virtual unsigned int PlayerSolidMask( bool brushOnly = false ) const; // returns the solid mask for the given player, so bots can have a more-restrictive set - CNetworkArray(Vector, m_vecBulletPositions, MAX_PLAYER_BULLET_POSITIONS); - CNetworkVar(int, m_iBulletPositionCount); }; typedef CHandle CBasePlayerHandle; diff --git a/game/server/player_command.cpp b/game/server/player_command.cpp index f4137db009..7fa3213cea 100644 --- a/game/server/player_command.cpp +++ b/game/server/player_command.cpp @@ -84,9 +84,9 @@ void CPlayerMove::FinishCommand( CBasePlayer *player ) { VPROF( "CPlayerMove::FinishCommand" ); - player->m_pCurrentCommand = NULL; - CBaseEntity::SetPredictionRandomSeed( NULL ); - CBaseEntity::SetPredictionPlayer( NULL ); + // player->m_pCurrentCommand = NULL; + // CBaseEntity::SetPredictionRandomSeed( NULL ); + // CBaseEntity::SetPredictionPlayer( NULL ); } //----------------------------------------------------------------------------- diff --git a/game/shared/cstrike/cs_player_shared.cpp b/game/shared/cstrike/cs_player_shared.cpp index 303f6fee27..c10fe32147 100644 --- a/game/shared/cstrike/cs_player_shared.cpp +++ b/game/shared/cstrike/cs_player_shared.cpp @@ -183,20 +183,14 @@ float CCSPlayer::GetPlayerMaxSpeed() return speed; } -float g_bulletDiameter = 0.0f; -static constexpr float MMToUnits(float mm) -{ - return (mm / 10.f) / 1.905f; -} - -void UTIL_ClipTraceToPlayersHull( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, ITraceFilter *filter, trace_t *tr ) +void UTIL_ClipTraceToPlayersHull( float flBulletDiameter, const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, ITraceFilter *filter, trace_t *tr ) { trace_t playerTrace; Ray_t ray; float smallestFraction = tr->fraction; const float maxRange = 60.0f; - ray.Init( vecAbsStart, vecAbsEnd , Vector(-g_bulletDiameter, -g_bulletDiameter, -g_bulletDiameter), Vector(g_bulletDiameter, g_bulletDiameter, g_bulletDiameter) ); + ray.Init( vecAbsStart, vecAbsEnd , Vector(-flBulletDiameter, -flBulletDiameter, -flBulletDiameter), Vector(flBulletDiameter, flBulletDiameter, flBulletDiameter) ); for ( int k = 1; k <= gpGlobals->maxClients; ++k ) { @@ -227,66 +221,109 @@ void UTIL_ClipTraceToPlayersHull( const Vector& vecAbsStart, const Vector& vecAb } } +float CCSPlayer::GetBulletDiameter(int iBulletType) +{ + auto MMToUnits = [] (float&& mm) + { + return (mm / 10.f) / 1.905f; + }; + + if (IsAmmoType(iBulletType, BULLET_PLAYER_50AE)) + { + return MMToUnits(13.8f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_762MM)) + { + return MMToUnits(7.62f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_556MM) + || IsAmmoType(iBulletType, BULLET_PLAYER_556MM_BOX)) + { + return MMToUnits(5.56f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_338MAG)) + { + return MMToUnits(8.6f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_9MM)) + { + return MMToUnits(9.f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_BUCKSHOT)) + { + return MMToUnits(9.9f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_45ACP)) + { + return MMToUnits(11.43f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_357SIG)) + { + return MMToUnits(9.f); + } + else if (IsAmmoType(iBulletType, BULLET_PLAYER_57MM)) + { + return MMToUnits(5.7f); + } + else + { + Assert(false); + return 0.0f; + } +} + void CCSPlayer::GetBulletTypeParameters( int iBulletType, float &fPenetrationPower, - float &flPenetrationDistance ) + float &flPenetrationDistance, + float &flBulletDiameter ) { //MIKETODO: make ammo types come from a script file. if ( IsAmmoType( iBulletType, BULLET_PLAYER_50AE ) ) { fPenetrationPower = 30; flPenetrationDistance = 1000.0; - g_bulletDiameter = MMToUnits(13.8f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_762MM ) ) { fPenetrationPower = 39; flPenetrationDistance = 5000.0; - g_bulletDiameter = MMToUnits(7.62f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_556MM ) || IsAmmoType( iBulletType, BULLET_PLAYER_556MM_BOX ) ) { fPenetrationPower = 35; flPenetrationDistance = 4000.0; - g_bulletDiameter = MMToUnits(5.56f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_338MAG ) ) { fPenetrationPower = 45; flPenetrationDistance = 8000.0; - g_bulletDiameter = MMToUnits(8.6f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_9MM ) ) { fPenetrationPower = 21; flPenetrationDistance = 800.0; - g_bulletDiameter = MMToUnits(9.f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_BUCKSHOT ) ) { fPenetrationPower = 0; flPenetrationDistance = 0.0; - g_bulletDiameter = MMToUnits(9.9f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_45ACP ) ) { fPenetrationPower = 15; flPenetrationDistance = 500.0; - g_bulletDiameter = MMToUnits(11.43f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_357SIG ) ) { fPenetrationPower = 25; flPenetrationDistance = 800.0; - g_bulletDiameter = MMToUnits(9.f); } else if ( IsAmmoType( iBulletType, BULLET_PLAYER_57MM ) ) { fPenetrationPower = 30; flPenetrationDistance = 2000.0; - g_bulletDiameter = MMToUnits(5.7f); } else { @@ -294,7 +331,9 @@ void CCSPlayer::GetBulletTypeParameters( Assert( false ); fPenetrationPower = 0; flPenetrationDistance = 0.0; - } + } + + flBulletDiameter = GetBulletDiameter(iBulletType); } static void GetMaterialParameters( int iMaterial, float &flPenetrationModifier, float &flDamageModifier ) @@ -365,16 +404,16 @@ static bool TraceToExit(Vector &start, Vector &dir, Vector &end, float flStepSiz return false; } -inline void UTIL_TraceLineIgnoreTwoEntities( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, +inline void UTIL_TraceLineIgnoreTwoEntities(float flBulletDiameter, const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask, const IHandleEntity *ignore, const IHandleEntity *ignore2, int collisionGroup, trace_t *ptr ) { Ray_t ray; - ray.Init( vecAbsStart, vecAbsEnd, Vector(-g_bulletDiameter, -g_bulletDiameter, -g_bulletDiameter), Vector(g_bulletDiameter, g_bulletDiameter, g_bulletDiameter) ); + ray.Init( vecAbsStart, vecAbsEnd, Vector(-flBulletDiameter, -flBulletDiameter, -flBulletDiameter), Vector(flBulletDiameter, flBulletDiameter, flBulletDiameter) ); CTraceFilterSkipTwoEntities traceFilter( ignore, ignore2, collisionGroup ); enginetrace->TraceRay( ray, mask, &traceFilter, ptr ); if( r_visualizetraces.GetBool() ) { - NDebugOverlay::SweptBox( ptr->startpos, ptr->endpos, Vector(-g_bulletDiameter, -g_bulletDiameter, -g_bulletDiameter), Vector(g_bulletDiameter, g_bulletDiameter, g_bulletDiameter), QAngle(), 255, 0, 0, true, 100.0f ); + NDebugOverlay::SweptBox( ptr->startpos, ptr->endpos, Vector(-flBulletDiameter, -flBulletDiameter, -flBulletDiameter), Vector(flBulletDiameter, flBulletDiameter, flBulletDiameter), QAngle(), 255, 0, 0, true, 100.0f ); } } @@ -401,15 +440,14 @@ void CCSPlayer::FireBullet( float flPenetrationPower = 0; // thickness of a wall that this bullet can penetrate float flPenetrationDistance = 0; // distance at which the bullet is capable of penetrating a wall float flDamageModifier = 0.5; // default modification of bullets power after they go through a wall. - float flPenetrationModifier = 1.f; - - GetBulletTypeParameters( iBulletType, flPenetrationPower, flPenetrationDistance ); + float flPenetrationModifier = 1.f; + float flBulletDiameter = 0.0f; + + GetBulletTypeParameters( iBulletType, flPenetrationPower, flPenetrationDistance, flBulletDiameter ); if ( !pevAttacker ) pevAttacker = this; // the default attacker is ourselves - int iPenetrationCount = 0; - if ( weapon_accuracy_nospread.GetBool() ) { xSpread = 0.0f; @@ -464,6 +502,7 @@ void CCSPlayer::FireBullet( bool bFirstHit = true; CBasePlayer *lastPlayerHit = NULL; + MDLCACHE_CRITICAL_SECTION(); if ( m_pCurrentCommand->debug_hitboxes == CUserCmd::DEBUG_HITBOXES_ON_BULLET ) { @@ -483,21 +522,21 @@ void CCSPlayer::FireBullet( } #endif } - } - MDLCACHE_CRITICAL_SECTION(); + } + while ( fCurrentDamage > 0 ) { Vector vecEnd = vecSrc + vecDir * flDistance; trace_t tr; // main enter bullet trace - UTIL_TraceLineIgnoreTwoEntities( vecSrc, vecEnd, CS_MASK_SHOOT|CONTENTS_HITBOX, this, lastPlayerHit, COLLISION_GROUP_NONE, &tr ); + UTIL_TraceLineIgnoreTwoEntities(flBulletDiameter, vecSrc, vecEnd, CS_MASK_SHOOT|CONTENTS_HITBOX, this, lastPlayerHit, COLLISION_GROUP_NONE, &tr ); { CTraceFilterSkipTwoEntities filter( this, lastPlayerHit, COLLISION_GROUP_NONE ); // Check for player hitboxes extending outside their collision bounds const float rayExtension = 40.0f; - UTIL_ClipTraceToPlayersHull( vecSrc, vecEnd + vecDir * rayExtension, CS_MASK_SHOOT|CONTENTS_HITBOX, &filter, &tr ); + UTIL_ClipTraceToPlayersHull(flBulletDiameter, vecSrc, vecEnd + vecDir * rayExtension, CS_MASK_SHOOT|CONTENTS_HITBOX, &filter, &tr ); } lastPlayerHit = ToBasePlayer(tr.m_pEnt); @@ -512,22 +551,6 @@ void CCSPlayer::FireBullet( bFirstHit = false; -#ifndef CLIENT_DLL - // - // Propogate a bullet impact event - // @todo Add this for shotgun pellets (which dont go thru here) - // - IGameEvent * event = gameeventmanager->CreateEvent( "bullet_impact" ); - if ( event ) - { - event->SetInt( "userid", GetUserID() ); - event->SetFloat( "x", tr.endpos.x ); - event->SetFloat( "y", tr.endpos.y ); - event->SetFloat( "z", tr.endpos.z ); - gameeventmanager->FireEvent( event ); - } -#endif - /************* MATERIAL DETECTION ***********/ surfacedata_t *pSurfaceData = physprops->GetSurfaceData( tr.surface.surfaceProps ); int iEnterMaterial = pSurfaceData->game.material; @@ -549,24 +572,34 @@ void CCSPlayer::FireBullet( { #ifdef CLIENT_DLL // draw red client impact markers - debugoverlay->AddBoxOverlay( tr.endpos, Vector(-g_bulletDiameter,-g_bulletDiameter,-g_bulletDiameter), Vector(g_bulletDiameter,g_bulletDiameter,g_bulletDiameter), QAngle( 0, 0, 0), 255,0,0,127, 60 ); + debugoverlay->AddBoxOverlay( tr.endpos, Vector(-flBulletDiameter,-flBulletDiameter,-flBulletDiameter), Vector(flBulletDiameter,flBulletDiameter,flBulletDiameter), QAngle( 0, 0, 0), 255,0,0,127, 60 ); if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { C_BasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->DrawClientHitboxes( 60, true ); player->m_nTickBaseFireBullet = int(player->GetTimeBase() / TICK_INTERVAL); - } + } #else - m_vecBulletPositions.Set(iPenetrationCount, tr.endpos); - iPenetrationCount++; - m_iBulletPositionCount.Set(iPenetrationCount); - if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() ) { CBasePlayer *player = ToBasePlayer( tr.m_pEnt ); player->RecordServerHitboxes( this ); + } + + // + // Propogate a bullet impact event + // @todo Add this for shotgun pellets (which dont go thru here) + // + IGameEvent * event = gameeventmanager->CreateEvent( "bullet_impact" ); + if ( event ) + { + event->SetFloat( "x", tr.endpos.x ); + event->SetFloat( "y", tr.endpos.y ); + event->SetFloat("z", tr.endpos.z); + event->SetFloat( "diameter", flBulletDiameter ); + gameeventmanager->FireEvent( event ); } #endif } @@ -592,7 +625,7 @@ void CCSPlayer::FireBullet( if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) { trace_t waterTrace; - UTIL_TraceHull( vecSrc, tr.endpos, Vector(-g_bulletDiameter, -g_bulletDiameter, -g_bulletDiameter), Vector(g_bulletDiameter, g_bulletDiameter, g_bulletDiameter), (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &waterTrace ); + UTIL_TraceHull( vecSrc, tr.endpos, Vector(-flBulletDiameter, -flBulletDiameter, -flBulletDiameter), Vector(flBulletDiameter, flBulletDiameter, flBulletDiameter), (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &waterTrace ); if( waterTrace.allsolid != 1 ) { @@ -675,12 +708,12 @@ void CCSPlayer::FireBullet( // find exact penetration exit trace_t exitTr; - UTIL_TraceHull( penetrationEnd, tr.endpos, Vector(-g_bulletDiameter, -g_bulletDiameter, -g_bulletDiameter), Vector(g_bulletDiameter, g_bulletDiameter, g_bulletDiameter), CS_MASK_SHOOT|CONTENTS_HITBOX, NULL, &exitTr ); + UTIL_TraceHull( penetrationEnd, tr.endpos, Vector(-flBulletDiameter, -flBulletDiameter, -flBulletDiameter), Vector(flBulletDiameter, flBulletDiameter, flBulletDiameter), CS_MASK_SHOOT|CONTENTS_HITBOX, NULL, &exitTr ); if( exitTr.m_pEnt != tr.m_pEnt && exitTr.m_pEnt != NULL ) { // something was blocking, trace again - UTIL_TraceHull( penetrationEnd, tr.endpos, Vector(-g_bulletDiameter, -g_bulletDiameter, -g_bulletDiameter), Vector(g_bulletDiameter, g_bulletDiameter, g_bulletDiameter), CS_MASK_SHOOT|CONTENTS_HITBOX, exitTr.m_pEnt, COLLISION_GROUP_NONE, &exitTr ); + UTIL_TraceHull( penetrationEnd, tr.endpos, Vector(-flBulletDiameter, -flBulletDiameter, -flBulletDiameter), Vector(flBulletDiameter, flBulletDiameter, flBulletDiameter), CS_MASK_SHOOT|CONTENTS_HITBOX, exitTr.m_pEnt, COLLISION_GROUP_NONE, &exitTr ); } // get material at exit point